Retrofit 里用到的一些设计模式

作者: Ava_T | 来源:发表于2020-03-30 21:58 被阅读0次

    设计模式是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。根据其目的分为创建型 Creational、结构型 Structual、行为型 Behavioral。

    为了提高软件系统的可维护性和可复用性,增加软件的可扩展性和灵活性,开发时要尽量根据 7 条原则来开发程序,从而提高软件开发效率、节约软件开发成本和维护成本。分别是

    • 开闭原则(Open Closed Principle,OCP):软件实体应当对扩展开放,对修改关闭(Software entities should be open for extension,but closed for modification)。

    • 里氏替换原则(Liskov Substitution Principle,LSP):继承必须确保超类所拥有的性质在子类中仍然成立(Inheritance should ensure that any property proved about supertype objects also holds for subtype objects)。里氏替换原则主要阐述了有关继承的一些原则,也就是什么时候应该使用继承,什么时候不应该使用继承,以及其中蕴含的原理。里氏替换原是继承复用的基础,它反映了基类与子类之间的关系,是对开闭原则的补充,是对实现抽象化的具体步骤的规范。

    • 依赖倒置原则(Dependence Inversion Principle,DIP):高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象(High level modules shouldnot depend upon low level modules.Both should depend upon abstractions.Abstractions should not depend upon details. Details should depend upon abstractions)。其核心思想是:要面向接口编程,不要面向实现编程。依赖倒置原则是实现开闭原则的重要途径之一,降低了客户与实现模块之间的耦合。

    • 单一职责原则(Single Responsibility Principle,SRP):这里的职责是指类变化的原因,单一职责原则规定一个类应该有且仅有一个引起它变化的原因,否则类应该被拆分(There should never be more than one reason for a class to change)。对象不应该承担太多职责。

    • 接口隔离原则(Interface Segregation Principle,ISP):客户端不应该被迫依赖于它不使用的方法(Clients should not be forced to depend on methods they do not use)。该原则还有另外一个定义:一个类对另一个类的依赖应该建立在最小的接口上(The dependency of one class to another one should depend on the smallest possible interface)。
      以上两个定义的含义是:要为各个类建立它们需要的专用接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。
      接口隔离原则和单一职责都是为了提高类的内聚性、降低它们之间的耦合性,体现了封装的思想。

    • 迪米特法则(Law of Demeter,LoD):只与你的直接朋友交谈,不跟“陌生人”说话(Talk only to your immediate friends and not to strangers)。其含义是:如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。
      “朋友”是指:当前对象本身、当前对象的成员对象、当前对象所创建的对象、当前对象的方法参数等,这些对象同当前对象存在关联、聚合或组合关系,可以直接访问这些对象的方法。

    • 合成复用原则(Composite Reuse Principle,CRP):要求在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。如果要使用继承关系,则必须严格遵循里氏替换原则。合成复用原则同里氏替换原则相辅相成的,两者都是开闭原则的具体实现规范。

    Retrofit

    Retrofit 是一个类型安全的 Http 客户端,主要有以下功能特点

    1. 将 Http 请求对象化,函数化。让接口的函数代表具体请求。
    2. 利用注解的方式标记参数,将 HTTP 的请求方法,请求头,请求参数,请求体等等都用注解的方式标记,使用起来非常方便。
    3. 支持 Multipart,以及文件上传(file upload)。
    4. 直接将 Http 的 Response 转换成对象。用户可以根据 Response 的具体内容,更换转换器,或者自己新建转化器。
    5. Retrofit 默认使用 OkHttp 开源库请求后台,用户也可以使用自定义的具体请求方式。方便扩展。
    6. 自带提供了异步处理 Http 请求的方式。

    我们结合 Retrofit 来看看他使用了什么设计模式,是如何实现解耦的。

    先来看看 Retrofit 的使用流程:

    Retrofit adapts a Java interface to HTTP calls by using annotations on the declared methods to
    define how requests are made. Create instances using the builder and pass your interface to create to generate an implementation.

    将HTTP请求抽象成java接口类,用注解描述和配置网络请求参数,封装Url地址和网络数据请求。通过new Retrofit.Builder().build() 构建 Retrofit 实例对象,同时进行具体配置。

    public interface GitHubService {
      @GET("users/{user}/repos")
      Call<List<Repo>> listRepos(@Path("user") String user);
    }
    

    Create an implementation of the API defined by the service interface.

    Retrofit.create() 返回了一个 Service Interface 的 proxy。

    Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("https://api.github.com/")
        .build();
    
    GitHubService service = retrofit.create(GitHubService.class);
    

    The Retrofit class generates an implementation of the GitHubService interface.Each Call from the created GitHubService can make a synchronous or asynchronous HTTP request to the remote webserver.

    Call<List<Repo>> repos = service.listRepos("octocat");
    

    网络请求流程:

    1. 通过解析网络请求接口的注解配置网络请求参数

    2. 通过动态代理生成网络请求对象

    3. 通过网络请求适配器网络请求对象进行平台适配

    4. 通过网络请求执行器发送网络请求

    5. 通过数据转换器解析服务器返回的数据

    6. 通过回调执行器切换线程(子线程 ->>主线程)

    7. 用户在主线程处理返回结果

    这是别人画的一张图,基本都标出来了,简单的工厂、建造者、单例没有标。

    网上大佬画的图

    外观模式 Facade

    定义:

    系统外部与一个子系统的通信必须通过一个统一的外观对象进行,为子系统中的一组接口提供一个一致的界面。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。外观模式又称为门面模式,它是一种对象结构型模式。

    Retrofit 对客户端模块提供统一接口,Retrofit 类内部封装了 ServiceMethod、CallAdapter 和 Converter 等组件。并且 CallAdapter 和 Converter 都是抽象为接口,用户可以扩展自定义的实现。

    public final class Retrofit {
      private final Map<Method, ServiceMethod<?, ?>> serviceMethodCache = new ConcurrentHashMap<>();
    
      final okhttp3.Call.Factory callFactory;
      final HttpUrl baseUrl;
      final List<Converter.Factory> converterFactories;
      final List<CallAdapter.Factory> callAdapterFactories;
      final @Nullable Executor callbackExecutor;
      final boolean validateEagerly;
    
      Retrofit(okhttp3.Call.Factory callFactory, HttpUrl baseUrl,
          List<Converter.Factory> converterFactories, List<CallAdapter.Factory> callAdapterFactories,
          @Nullable Executor callbackExecutor, boolean validateEagerly) {
        this.callFactory = callFactory;
        this.baseUrl = baseUrl;
        this.converterFactories = converterFactories; // Copy+unmodifiable at call site.
        this.callAdapterFactories = callAdapterFactories; // Copy+unmodifiable at call site.
        this.callbackExecutor = callbackExecutor;
        this.validateEagerly = validateEagerly;
      }
        /*...*/
    }
    

    动态代理 Proxy

    代理模式是结构型模式。当无法或不想直接访问某个对象,或者访问某个对象比较复杂的时候,可以通过一个代理对象来间接访问,代理对象向客户端提供和真实对象同样的接口功能。经典设计模式中,代理模式有四种角色:

    • Subject 抽象主题类——申明代理对象和真实对象共同的接口方法;
    • RealSubject 真实主题类——实现了 Subject 接口,真实执行业务逻辑的地方;
    • ProxySubject 代理类——实现了 Subject 接口,持有对 RealSubject 的引用,在实现的接口方法中调用 RealSubject 中相应的方法执行;
    • Cliect 客户端类——使用代理对象的类。
    public class Client
    {
        public static void main(String[] args)
        {
            ProxySubject proxy=new ProxySubject();
            proxy.request();
        }
    }
    //抽象主题
    interface Subject
    {
        void request();
    }
    //真实主题
    class RealSubject implements Subject
    {
        public void request()
        {
            System.out.println("访问真实主题方法...");
        }
    }
    //代理
    class ProxySubject implements Subject
    {
        private RealSubject realSubject;
        public void request()
        {
            if (realSubject==null)
            {
                realSubject=new RealSubject();
            }
            preRequest();
            realSubject.request();
            postRequest();
        }
        public void preRequest()
        {
            System.out.println("访问真实主题之前的预处理。");
        }
        public void postRequest()
        {
            System.out.println("访问真实主题之后的后续处理。");
        }
    }
    

    代理模式分为静态代理动态代理,严格按照上述角色定义编写的代码属于静态代理,即在代码运行前 ProxySubject 代理类的 .class 编译文件就已存在。Retrofit使用的是动态代理,是通过反射机制来动态生成方法接口的代理对象的。动态代理的实现是通过 JDK 提供的 InvocationHandler 接口,实现该接口重写其调用方法 invoke。

    反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 java 语言的反射机制。

    先看老师给的示例代码()

    定义 Isubject 接口,里面有两个方法

    public interface ISubject {
       public void request();
       public void output();
    }
    

    RealSubject 实现接口,是真实主题

    public class RealSubject implements ISubject {
    
       @Override
       public void request() {
          System.out.println("从房主处租房");
    
       }
       
       public void output(){
          try {
             Thread.sleep(100);
             System.out.println("装修");
          } catch (InterruptedException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
          }
       }
    
    }
    

    动态代理类,实现 InvocationHandler 接口,重写 invoke 方法中体现了代理类的工作

    /**
     * 该代理类的内部属性是Object类型,实际使用的时候通过该类的构造方法传递进来一个对象
     * 此外,该类还实现了invoke方法,该方法中的method.invoke其实就是调用被代理对象的将来执行的方法,
     * 方法参数是obj,表示该方法从属于obj,通过动态代理类,我们可以在执行真是对象的方法前后加入额外的一些方法
     * @author Zan Wang
     *
     */
    public class DynamicSubject implements InvocationHandler {
       private Object obj;
       Date t1, t2;
       long dateDiff;
       
       public DynamicSubject(Object obj) {
          this.obj = obj;
       } 
    
    // Object proxy:代理对象
    // Method method:目标类要执行的方法,
    // Object[] args:方法中的参数
       @Override
       public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
          t1 = new Date(System.currentTimeMillis());
          System.out.println(method.getName() + "操作前的时间: " + new SimpleDateFormat("yyyy/MM/dd-HH:mm:ss:SSS").format(t1));
          
          method.invoke(obj, args);//真实主题的工作
          
          t2 = new Date(System.currentTimeMillis());
          System.out.println(method.getName() + "操作后的时间: " + new SimpleDateFormat("yyyy/MM/dd-HH:mm:ss:SSS").format(t2));
          
          dateDiff = t2.getTime() - t1.getTime(); 
          System.out.println(dateDiff + "毫秒完成了操作");
          return null;
       }
    
    }
    
    public class DynamicClient {
    
       public static void main(String[] args) {
          System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
          System.out.println(System.getProperties().get("sun.misc.ProxyGenerator.saveGeneratedFiles"));
          //真实主题 
          RealSubject realSubject = new RealSubject();
           //动态代理
          InvocationHandler handler = new DynamicSubject(realSubject);
           //获得类信息
          Class<?> clazz = handler.getClass();
          System.out.println("$Proxy0.class全名: "+Proxy.getProxyClass(clazz.getClassLoader(), realSubject.getClass().getInterfaces()));
          
          //下面的代码一次性生成代理
          ISubject subject = (ISubject)Proxy.newProxyInstance(clazz.getClassLoader(), 
                realSubject.getClass().getInterfaces(), handler);
          subject.request();
          subject.output();
          System.out.println(subject.getClass());
       }
    
    }
    

    再看看 Retrofit 的代码

    public <T> T create(final Class<T> service) {
      Utils.validateServiceInterface(service);
      if (validateEagerly) {
        eagerlyValidateMethods(service);
      }
        //返回动态代理类
      return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
          new InvocationHandler() {
            private final Platform platform = Platform.get();
    
              //处理 method 分为三种情况:Object 的方法,直接返回 Object 的实现;判断是否 Java8 支持的 DefaultMethod;或创建 OkHttpCall,通过 ServiceMethod 转换为接口的动态代理类。
            @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
                throws Throwable {
              // If the method is a method from Object then defer to normal invocation.
              if (method.getDeclaringClass() == Object.class) {
                return method.invoke(this, args);
              }
              if (platform.isDefaultMethod(method)) {
                return platform.invokeDefaultMethod(method, service, proxy, args);
              }
              ServiceMethod<Object, Object> serviceMethod =
                  (ServiceMethod<Object, Object>) loadServiceMethod(method);
              OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
              return serviceMethod.adapt(okHttpCall);
            }
          });
    }
    

    使用 Retrofit 的客户端通过 create 方法获取自定义 HTTP 请求的动态代理类,是客户端代码中最重要的部分之一。这里有三个重要组件:

    • ServiceMethod
    • OkHttpCall
    • ServiceMethod.callAdapter

    通过 ServiceMethod 来解析 method,通过解析注解、参数,将它们封装成我们所熟悉的 request。然后通过具体的返回值类型,让之前配置的工厂生成具体的 CallAdapter、ResponseConverter。得到这个 ServiceMethod 之后,传给 OkHttpCall,这个 OkHttpCall 就是对 Okhttp 的网络请求封装的一个类。用它跟 OkHttp 对接,所有 OkHttp 需要的参数都可以看这个类。serviceMethod.callAdapter.adapt(okHttpCall) 将 OkHttp 返回的CALL适配成应当返回的类型,默认返回。ServiceMethod 中如果没有配置 CallAdapter,则使用默认的 DefaultCallAdapterFactory, 得到的结果是 Call<?>。

    Retrofit 采用的代理模式和正常的代理模式并不一样,正常的代理模式是对真实对象的一层控制,这个真实对象是实现对应的接口的,而这里并没有真实的对象,只是拿到网络请求接口实例上所有注解,它把方法调用最终全部转发到 OkHttp 了,更加灵活。

    适配器模式 Adapter

    适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。适配器提供客户类需要的接口,适配器的实现就是把客户类的请求转化为对适配者的相应接口的调用。也就是说:当客户类调用适配器的方法时,在适配器类的内部将调用适配者类的方法,而这个过程对客户类是透明的,客户类并不直接访问适配者类。因此,适配器可以使由于接口不兼容而不能交互的类可以一起工作。这就是适配器模式的模式动机。

    • Target:目标抽象类
    • Adapter:适配器类
    • Adaptee:适配者类
    • Client:客户端类

    适配器模式有类的适配器模式对象的适配器模式两种不同的形式。对象的适配器模式把被适配的类的 API 转换成为目标类的 API,与类的适配器模式不同的是,对象的适配器模式不是使用继承关系连接到 Adaptee 类,而是使用委派关系连接到 Adaptee 类。

    UML 图

    从上图可以看出,Adaptee 类并没有 sampleOperation2() 方法,而客户端则期待这个方法。为使客户端能够使用 Adaptee 类,需要提供一个包装 (Wrapper) 类 Adapter。这个包装类包装了一个 Adaptee 的实例,从而此包装类能够把 Adaptee 的 API 与 Target 类的 API 衔接起来。Adapter 与 Adaptee 是委派关系,这决定了适配器模式是对象的。

    public interface Target {
        /**
         * 这是源类 Adaptee 也有的方法
         */
        public void sampleOperation1(); 
        /**
         * 这是源类 Adapteee 没有的方法
         */
        public void sampleOperation2(); 
    }
    public class Adaptee {
    
        public void sampleOperation1(){}
    
    }
    public class Adapter {
        private Adaptee adaptee;
    
        public Adapter(Adaptee adaptee){
            this.adaptee = adaptee;
        }
        /**
         * 源类 Adaptee 有方法 sampleOperation1
         * 因此适配器类直接委派即可
         */
        public void sampleOperation1(){
            this.adaptee.sampleOperation1();
        }
        /**
         * 源类 Adaptee 没有方法 sampleOperation2
         * 因此由适配器类需要补充此方法
         */
        public void sampleOperation2(){
            //写相关的代码
        }
    }
    

    类的适配器:

    public class Adapter extends Adaptee implements Target {
        /**
         * 由于源类 Adaptee 没有方法 sampleOperation2()
         * 因此适配器补充上这个方法
         */
        @Override
        public void sampleOperation2() {
            //写相关的代码
        }
    }
    

    那么看看 Retrofit 中的 CallAdapter 的接口。这里的 Call 是 OkHttpCall。即让已经存在的OkHttpCall,被不同的标准、平台来调用。设计了这个接口CallAdapter,让其他平台做不同的实现来转换。

    /**
     * Adapts a {@link Call} with response type {@code R} into the type of {@code T}. Instances are
     * created by {@linkplain Factory a factory} which is
     * {@linkplain Retrofit.Builder#addCallAdapterFactory(Factory) installed} into the {@link Retrofit}
     * instance.
     */
    public interface CallAdapter<R, T> {
    
      Type responseType();
    
      T adapt(Call<R> call);
    
      /**
       * Creates {@link CallAdapter} instances based on the return type of {@linkplain
       * Retrofit#create(Class) the service interface} methods.
       */
      abstract class Factory {
    
        public abstract @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations,
            Retrofit retrofit);
    
        protected static Type getParameterUpperBound(int index, ParameterizedType type) {
          return Utils.getParameterUpperBound(index, type);
        }
    
        protected static Class<?> getRawType(Type type) {
          return Utils.getRawType(type);
        }
      }
    }
    

    Call 接口是 Retrofit 内置的发送请求给服务器并且返回响应体的调用接口,包括同步、异步请求,查询、取消、复制等功能。

    public interface Call<T> extends Cloneable {
        // 同步执行请求
        Response<T> execute() throws IOException;
        // 异步执行请求
        void enqueue(Callback<T> callback);
        // 省略代码
    
        // 取消请求
        void cancel();
        // 复制请求
        Call<T> clone();
    }
    

    如果客户端没有配置 CallAdapter,Retrofit会采用默认的实现 DefaultCallAdapterFactory 直接返回Call对象,而如果配置了 RxJava 的 RxJavaCallAdapterFactory 实现,就会将 Call<R> 转换为 Observable<R>,供客户端调用。

    static final class SimpleCallAdapter implements CallAdapter<Observable<?>> {
        // 省略代码
        @Override 
        public <R> Observable<R> adapt(Call<R> call) {
          Observable<R> observable = Observable.create(new CallOnSubscribe<>(call))
              .lift(OperatorMapResponseToBodyOrError.<R>instance());
          if (scheduler != null) {
            return observable.subscribeOn(scheduler);
          }
          return observable;
        }
      }
    

    总结下,CallAdapter 对应 Target,其 adapt 方法返回客户端类 Client 需要的对象;RxJavaCallAdapterFactory 的 get 方法返回 SimpleCallAdapter 对象(或 ResultCallAdapter 对象)实现了 CallAdapter<Observable<?>>,对应 Adapter;Call <R>对应 Adaptee 适配者类,包含需要被适配的方法。

    值得说明的是,这里 SimpleCallAdapter 并没有通过域的方式持有Call<R>,而是直接在 CallAdapter 的 get 方法中将 Call<R> 以入参形式传入。虽然并不是教科书式的对象适配器模式,但使用却更加灵活、方便。

    工厂模式 Factory

    这里把简单工厂模式、工厂模式和抽象工厂模式放一起了,它们都属于创建型模式,其主要功能都是将对象的实例化部分抽取出来。

    简单工厂模式

    只需要传给工厂一些参数信息,工厂解析参数返回相应的产品。对外隐藏产品细节,逻辑简单。

    以 Platform 类为例,其首先包含静态域 PLATFORM,并通过静态返回供客户端调用。

    private static final Platform PLATFORM = findPlatform();
    
    static Platform get() {
        return PLATFORM;
    }
    

    findPlatform 其实就是一个静态工厂方法,根据 Class.forName 是否抛出 ClassNotFoundException 来判断不同的平台。

    private static Platform findPlatform() {
        try {
            Class.forName("android.os.Build");
            if (Build.VERSION.SDK_INT != 0) {
                return new Android();
            }
        } catch (ClassNotFoundException ignored) {
        }
        try {
            Class.forName("java.util.Optional");
            return new Java8();
        } catch (ClassNotFoundException ignored) {
        }
        try {
            Class.forName("org.robovm.apple.foundation.NSObject");
            return new IOS();
        } catch (ClassNotFoundException ignored) {
        }
        return new Platform();
    }
    

    而Android、Java8、IOS 相当于 ConcreteProduct 的角色,继承自抽象产品类Platform。

    Java8:

    static class Java8 extends Platform {}
    

    Android:

    static class Android extends Platform {}
    

    IOS:

    static class IOS extends Platform {}
    

    工厂模式

    工厂模式对简单工厂中的工厂类进行了抽象。一个工厂只生产一种产品,所有的工厂都实现同一个抽象接口。工厂可以自主确定创建何种产品;符合开闭原则,增加新产品时,只需增加新类。

    还是 CallAdapter 的代码,CallAdapter.Factory 对应Factory抽象工厂类,包含两个静态工具方法 getParameterUpperBound、getRawType 和抽象方法 get。

    public interface CallAdapter<T> {
    
        Type responseType();
        <R> T adapt(Call<R> call);
     
        abstract class Factory {
            public abstract CallAdapter<?> get(Type returnType, Annotation[] annotations, Retrofit retrofit);
    
            protected static Type getParameterUpperBound(int index, ParameterizedType type) {
                return Utils.getParameterUpperBound(index, type);
            }
    
            protected static Class<?> getRawType(Type type) {
                return Utils.getRawType(type);
            }
        }
    }
    

    get 方法返回不同类型的 CallAdapter,比如 RxJavaCallAdapterFactory 返回 CallAdapter<Observable<?>>,DefaultCallAdapterFactory 返回 CallAdapter<Call<?>>。

    public final class RxJavaCallAdapterFactory extends CallAdapter.Factory {
    
        // 省略代码
        @Override
        public CallAdapter<?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
        
            // 省略代码
            CallAdapter<Observable<?>> callAdapter = getCallAdapter(returnType, scheduler);
            // 省略代码
            return callAdapter;
        }
    
        private CallAdapter<Observable<?>> getCallAdapter(Type returnType, Scheduler scheduler) {
            // 省略代码
        }
    }
    

    如果需要增加新的 CallAdapter,继承自 CallAdapter.Factory,覆盖 get 方法即可。符合面向对象软件设计的“开闭原则”。

    抽象工厂模式

    配置Retrofit时还需要配置 ConverterFactory,调用 OkHttp 时,将请求内容由T转换为 okhttp3.RequestBody,将返回内容由 okhttp3.ResponseBody 转换为 T,Converter 是就是负责转换的类。Retrofit官方文档中就给出了多种不同实现的转换器类,均继承自 Converter.Factory。

    相比于工厂模式,具体工厂负责生产具体的产品,每一个具体工厂对应一种具体产品,工厂方法也具有唯一性,一般情况下,一个具体工厂中只有一个工厂方法或者一组重载的工厂方法。但是有时候我们需要一个工厂可以提供多个产品对象,而不是单一的产品对象,例如上述 Converter.Factory 需要同时提供请求内容和返回内容的转换类,这时,就需要考虑抽象工厂模式。一个工厂只生产一种产品,不同的工厂可能实现不同的抽象工厂接口。

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

    以 GsonConverterFactory 为例进行说明,GsonConverterFactory 对应 ConcreteFactory 具体工厂,表示 Gson 转换类的工厂,GsonConverterFactory 继承自 AbstractFactory 抽象工厂——Converter.Factory,重写了 requestBodyConverter 方法和 responseBodyConverter 方法。

    public final class GsonConverterFactory extends Converter.Factory {
        // 省略代码
        @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);
        }
    }
    

    策略模式 Strategy

    完成一项任务,往往可以有多种不同的方式,每一种方式称为一个策略,我们可以根据环境或者条件的不同选择不同的策略来完成该项任务。针对这种情况,一种常规的做法是将多个策略写在一个类中,通过 if…else 或者 switch 等条件判断语句来选择具体的算法。这种方式实现简单、快捷,但维护成本很高,当添加新的策略时,需要修改源代码,这违背了开闭原则和单一原则。仍以 CallAdapter 为例,不同的 CallAdapter 代表着不同的策略,当我们调用这些不同的适配器的方法时,就能得到不同的结果,这就是策略模式。策略模式包含三种角色:

    • Context 上下文环境——区别于 Android 的 Context,这里代表操作策略的上下文;
    • Stragety 抽象策略——即不同策略需要实现的方法;
    • ConcreteStragety 策略实现——实现 Stragety 抽象策略。

    在 Retrofit中,配置 Retrofit.Builder 时 addCallAdapterFactory,配置的类就对应 Context;不同的 CallAdapter 都需要提供 adapt 方法,CallAdapter<T> 就对应 Stragety 抽象策略。RxJavaCallAdapterFactory 的 get 方法返回 SimpleCallAdapter 对象(或 ResultCallAdapter 对象)就对应具体的策略实现。

    通过 get 方法返回不同的 CallAdapter 对象;强调这些不同 CallAdapter 对象的 adapt 方法的具体实现。

    <R> T adapt(Call<R> call);
    

    工厂模式强调的是生产不同的对象,策略模式强调的是这些不同对象的策略方法的具体实现,是在创建对象之后。

    建造者模式 Builder

    建造者模式属于创建型模式,将构建复杂对象的过程和它的部件解耦,使构建过程和部件的表示隔离。Retrofit 内部包含 Retrofit.Builder,Retrofit 包含的域都能通过 Builder 进行构建。经典设计模式中建造者模式有四种角色:

    • Product 产品类——该类为一般为抽象类,定义 Product 的公共属性配置;
    • Builder 建造类——该类同样为抽象类,规范 Product 的组建,一般由子类实现具体 Product 的构建过程;
    • ConcreteBuilder 实际建造类——继承自 Builder,构建具体的 Product;
    • Director 组装类——统一组装过程。

    在 Retrofit 类中,Retrofit 直接对应 Product,并没有基于抽象 Product 进行扩展;Retrofit.Builder 对应 ConcreteBuilder,也没有基于抽象 Builder 进行扩展,同时省略了 Director,并在 Retrofit.Builder 每个 setter 方法都返回自身,使得客户端代码可以链式调用,整个构建过程更加简单。

    public static final class Builder {
      private final Platform platform;
      private @Nullable okhttp3.Call.Factory callFactory;
      private HttpUrl baseUrl;
      private final List<Converter.Factory> converterFactories = new ArrayList<>();
      private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();
      private @Nullable Executor callbackExecutor;
      private boolean validateEagerly;
    
      Builder(Platform platform) {
        this.platform = platform;
      }
    
      public Builder() {
        this(Platform.get());
      }
    
      Builder(Retrofit retrofit) {
        platform = Platform.get();
        callFactory = retrofit.callFactory;
        baseUrl = retrofit.baseUrl;
    
        converterFactories.addAll(retrofit.converterFactories);
        // Remove the default BuiltInConverters instance added by build().
        converterFactories.remove(0);
    
        callAdapterFactories.addAll(retrofit.callAdapterFactories);
        // Remove the default, platform-aware call adapter added by build().
        callAdapterFactories.remove(callAdapterFactories.size() - 1);
    
        callbackExecutor = retrofit.callbackExecutor;
        validateEagerly = retrofit.validateEagerly;
      }
    
      /**
       * Create the {@link Retrofit} instance using the configured values.
       * <p>
       * Note: If neither {@link #client} nor {@link #callFactory} is called a default {@link
       * OkHttpClient} will be created and used.
       */
      public Retrofit build() {
        if (baseUrl == null) {
          throw new IllegalStateException("Base URL required.");
        }
    
        okhttp3.Call.Factory callFactory = this.callFactory;
        if (callFactory == null) {
          callFactory = new OkHttpClient();
        }
    
        Executor callbackExecutor = this.callbackExecutor;
        if (callbackExecutor == null) {
          callbackExecutor = platform.defaultCallbackExecutor();
        }
    
        // Make a defensive copy of the adapters and add the default Call adapter.
        List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
        callAdapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor));
    
        // Make a defensive copy of the converters.
        List<Converter.Factory> converterFactories =
            new ArrayList<>(1 + this.converterFactories.size());
    
        // Add the built-in converter factory first. This prevents overriding its behavior but also
        // ensures correct behavior when using converters that consume all types.
        converterFactories.add(new BuiltInConverters());
        converterFactories.addAll(this.converterFactories);
    
        return new Retrofit(callFactory, baseUrl, unmodifiableList(converterFactories),
            unmodifiableList(callAdapterFactories), callbackExecutor, validateEagerly);
      }
    }
    

    装饰模式 Decorator

    装饰者模式以对客户透明的方式动态地给一个对象附加上更多的责任。换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰者模式可以在不使用创造更多子类的情况下,将对象的功能加以扩展。属于对象结构型模式。

    static final class ExecutorCallbackCall<T> implements Call<T> {
      final Executor callbackExecutor;
      final Call<T> delegate;
    
      ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
        this.callbackExecutor = callbackExecutor;
        this.delegate = delegate;
      }
    }
    

    可以将ExecutorCallbackCall当作是装饰角色,而真正去执行请求的是OkHttpCall。之所以要有个装饰类,是希望在原有类操作时去做一些额外操作。这里的操作就是线程转换,将子线程切换到主线程上去。

    观察者模式 Observer

    所有与网络请求相关的库一定会支持请求的异步发送,通过在库内部维护一个队列,将请求添加到该队列,同时注册一个回调接口,以便执行引擎完成该请求后,将请求结果进行回调。Retrofit 的网络请求执行引擎是 OkHttp,请求类是 OkHttpCall,其实现了 Call 接口,enqueue 方法如下,入参为 Callback 对象。

    void enqueue(Callback<T> callback);
    

    在 OkHttpCall 的 enqueue 实现方法中,通过在 okhttp3.Callback() 的回调方法中调用上述入参 Callback 对象的方法,实现通知观察者。

    @Override 
    public void enqueue(final Callback<T> callback) {
        // 省略代码
        call.enqueue(new okhttp3.Callback() {
            @Override 
            public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse)
              throws IOException {
                Response<T> response;
                try {
                    response = parseResponse(rawResponse);
                } catch (Throwable e) {
                    callFailure(e);
                    return;
                }
                callSuccess(response);
            }
    
        @Override 
        public void onFailure(okhttp3.Call call, IOException e) {
            try {
                callback.onFailure(OkHttpCall.this, e);
            } catch (Throwable t) {
                t.printStackTrace();
            }
        }
        private void callSuccess(Response<T> response) {
            try {
                callback.onResponse(OkHttpCall.this, response);
            } catch (Throwable t) {
                t.printStackTrace();
            }
       }
    

    总结下,Call 接口对应 Subject,定义被观察者的特性,包含 enqueue 等; OkHttpCall 对应 ConcreteSubject 具体被观察者,Callback 对应 Observer 抽象观察者,Callback 的实现类对应 ConcreteObserver 具体观察者。

    参考

    《设计模式:可复用面向对象软件的基础》

    https://www.jianshu.com/p/fb8d21978e38

    https://www.cnblogs.com/younghao/p/6078156.html

    https://www.cnblogs.com/younghao/p/6098329.html

    https://github.com/android-cn/android-open-project-analysis/tree/master/tool-lib/network/retrofit

    相关文章

      网友评论

        本文标题:Retrofit 里用到的一些设计模式

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