设计模式之工厂方法模式

作者: 树獭非懒 | 来源:发表于2018-12-27 15:08 被阅读10次

    工厂方法模式,顾名思义,调用工厂里的方法的模式。

    一、定义

    定义一个用于创建对象的接口,让子类决定实例化哪个类。主要用于生成复杂对象的地方。

    二、UML类图

    工厂模式类图.png

    三、解读类图

    1.抽象产品类--类图上的Product接口

    public abstract class Product {
        public abstract void product(); 
    }
    

    2.具体的产品类-类图上的ConcreteProduct

    public class ConcreteProduct extends Product{
        @Override
        public void product() {
            System.out.println("生产出一个产品");
        }
    }
    
    

    3.抽象的工厂类-类图上的Factory接口

    public abstract class Factory {
        public abstract Product create();
    }
    
    

    4.具体的工厂类-类图上的ConcreteFactory

    public class ConcreteFactory extends Factory{
        @Override
        public Product create() {
            return new ConcreteProduct();
        }
    }
    

    5.使用工厂类生产产品

    public class Client {
    
        public static void main(String[] args) {
            Factory factory=new ConcreteFactory();
            Product p=factory.create();
            p.product();
        }
    }
    

    输出结果:

    生产出一个产品

    以上是基本的使用方法。在你需要生产对象的时候可以直接使用具体工厂类的create方法产出你要的对象。在Client代码中是看不到具体的产品类ConcreteProduct,因为我们不需要知道内部的实现细节,不需要知道是怎么生产,这就是使用接口(抽象类)的好处,只需要调用暴露的接口方法就行了。

    与此同时代码的可拓展性也变强了。比如我想生产另外的对象,那么只需要换一个工厂就行了。

    比如换成2号工厂

    Factory factory=new ConcreteFactory2(); 
    

    2号工厂用于生产2号产品

    public class ConcreteFactory2 extends Factory{
        @Override
        public Product create() {
            return new ConcreteProduct2();
        }
    }
    
    public class ConcreteProduct2 extends Product{
        @Override
        public void product() {
            System.out.println("生产出一个2号产品");
        }
    }
    

    四、实现工厂方法的第二种方式

    有一种方式也比较常用,使用反射的方式去获取产品的对象

    1.定义一个工厂抽象类

    public abstract class ReflectFactory {
        public abstract<T extends Product> T createProduct(Class<T> clz);
    }
    

    2.具体工厂类

    public class ReflectConcreteFactory extends ReflectFactory{
    
        @Override
        public <T extends Product> T createProduct(Class<T> clz) {
            Product p=null;
            try {
                p=(Product) Class.forName(clz.getName()).newInstance();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return (T) p;
        }
    }
    
    

    3.使用工厂类生产

    public class ReflectClient {
        public static void main(String[] args) {
            ReflectFactory factory=new ReflectConcreteFactory();
            Product p=factory.createProduct(ConcreteProduct.class);
            p.product();
        }
    }
    

    这种方法和基本使用方法不同的是我在使用工厂生产对象的时候,要传入我想生产的那个具体的对象的类。表面看上去还不如之前的方法好,代码变复杂。

    但是这有个好处。只要这个具体产品是Product的子类,这些产品都可通过这一个工厂去实现。而之前的方式只能一个工厂对应一个具体的产品。

    简单的举个栗子:基本方法就是-可口可乐只能生产可乐,要生产芬达的饮料要芬达工厂去生产。反射的方法-可口可乐和芬达都是刺激性饮料,现在有个工厂专门生产刺激性饮料,但是你要什么可乐还是芬达饮料,你要先和工厂说,它会根据你的要求给你(也就对应传入的参数)

    总的来说,两种方式各有所长,在合适的时候选择合适的,才是最好的。

    五、Retrofit源码上工厂方法的应用

    1.RequestFactory

    (1)调用RequestFactory的create方法生产Request的对象。这个Request就是封装了网络请求信息的对象

    class RequestFactory {
      okhttp3.Request create(Object[] args) throws IOException {
        RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl,
            headers, contentType, hasBody, isFormEncoded, isMultipart);
    
        List<Object> argumentList = new ArrayList<>(argumentCount);
        for (int p = 0; p < argumentCount; p++) {
          argumentList.add(args[p]);
          handlers[p].apply(requestBuilder, args[p]);
        }
    
        return requestBuilder.get()  //建造者模式得到Request的对象
            .tag(Invocation.class, new Invocation(method, argumentList))
            .build();
      }
    }
    

    (2)在OkHttpCall中调用这个工厂生产对象

      private okhttp3.Call createRawCall() throws IOException {
        okhttp3.Call call = callFactory.newCall(requestFactory.create(args));
        if (call == null) {
          throw new NullPointerException("Call.Factory returned null.");
        }
        return call;
      }
    

    2.Converter.Factory

    在创建retrofit对象的时候,经常会添加了一个转换工厂,比如下面代码添加的GsonConverterFactory,是用于把数据转换成gson数据类型

      Retrofit retrofit=new Retrofit.Builder()
                    .baseUrl("http://api.github.com/")
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
    

    那么它是怎么使用工厂方法模式的呢?

    (1)抽象工厂类

    public interface Converter<F, T> {
        T convert(F value) throws IOException;
        public @Nullable Converter<ResponseBody, ?> responseBodyConverter(Type type,
            Annotation[] annotations, Retrofit retrofit) {
          return null;
        }、
         public @Nullable Converter<?, RequestBody> requestBodyConverter(Type type,
            Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
          return null;
        }
    }
    

    (2)具体的工厂类

    public final class GsonConverterFactory extends Converter.Factory {
      public static GsonConverterFactory create() {
        return create(new Gson());
      }
        
      public static GsonConverterFactory create(Gson gson) {
        if (gson == null) throw new NullPointerException("gson == null");
        return new GsonConverterFactory(gson);
      }
      
      //将响应体类型数据转换成?类型数据
      @Override
      public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
          Retrofit retrofit) {
        TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
        return new GsonResponseBodyConverter<>(gson, adapter);
      }
    
      //将?类型的数据转换成请求体类型的数据
      @Override
      public Converter<?, RequestBody> requestBodyConverter(Type type,
        Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
        TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
        return new GsonRequestBodyConverter<>(gson, adapter);
      }
    }
    

    咋一看看不出它生产的是什么对象。其实在重写的两个方法里,它通过不同的方法生产不同的对象,深入其中的一个requestBodyConverter方法去看

    class GsonRequestBodyConverter<T> implements Converter<T, RequestBody> {
        //...
        public RequestBody convert(T value) throws IOException {
        Buffer buffer = new Buffer();
        Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8);
        JsonWriter jsonWriter = gson.newJsonWriter(writer);
        adapter.write(jsonWriter, value);
        jsonWriter.close();
        return RequestBody.create(MEDIA_TYPE, buffer.readByteString());
      }
    }
    

    最后一行可以看到它返回了一个RequestBody的对象。这也符合了它的目的,就是将现有的类型的数据转换成请求体RequestBody类型的数据。

    六、总结

    总说看源码不能过于拘泥于细节实现,否则会看得满脸懵逼。但当你真正看的时候,特别是对应初学者,很难区分什么是细节实现,我刚在看Retrofit这部分源码的时候,看到这部分代码,emmn......这到底在干啥。最后我发现原因就是作者使用了大量的设计模式,在你不懂这些设计模式的时候,可不就是看天书一样。但当我去了解一些常用的设计模式基本实现之后,在去看这些代码,莫名其妙的就知道哪些是我不用关心的,比如上面的大多数代码,就为了创建一个对象,我只需要知道创建的对象是什么就行了。

    额外说一点

    Retrofit是一个很优秀的源码框架。里面用了很多设计模式,很适合花时间去学习。既可以学到Retrofit的源码实现,又能学到多种设计模式,想想就血赚啊~~

    当然之后也会有在Retrofit上使用的其他设计模式的文章。

    相关文章

      网友评论

        本文标题:设计模式之工厂方法模式

        本文链接:https://www.haomeiwen.com/subject/ffgrlqtx.html