从Retrofit/Okhttp源码 学习设计模式-建造者模式

Posted 涂程

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从Retrofit/Okhttp源码 学习设计模式-建造者模式相关的知识,希望对你有一定的参考价值。

建造者模式

首先我们先大致熟悉下通常的建造者模式

建造者模式(Builder Pattern)也叫生成器模式,其定义如下:将一个对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
建造者模式的通用类图如图所示:

Product产品类 Builder抽象建造者

  • 规范产品的组建,一般是由子类实现。

ConcreteBuilder

  • 实现抽象类定义的所有方法,并且返回一个组建好的对象。

Director导演类

产品类

public class Product {
  public 
  public void doSomething(){
    // 独立业务逻辑
  }
}

抽象建造者

public abstract class Builder {
  // 设置产品的不同成分,已获得不同的产品
  public abstract Builder setPart();
  // 建造产品
  public abstract Product buildProduct();
}
public class ConcreteProduct extends Builder {
  private Product product = new Product();

  public Builder setPart(){
    /*
     * 产品类的内部逻辑
     */
    ...
    return this;
  }

  // 创建一个产品
  public Product build(){
    return product;
  }
}

导演类

public class Director {
  private Builder builder = new ConcreteProduct();
  // 构建不同的产品
  public Product getAProduct(){
    return build.setPart().build();
  }
}

建造者模式的优点:

  • 封装性

使用建造者模式可以使客户端不必知道产品内部组成的细节。

  • 建造者独立,容易扩展
  • 便于控制细节风险

建造者模式的使用场景

  • 相同的方法,不同的执行顺序,产生不同的事件结果时,可以采用建造者模式
  • 多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时,则可以使用该模式。
  • 产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能,这个时候使用建造者模式非常合适
  • 在对象创建过程中会使用到系统中的一些其他对象,这些对象在产品对象的创建过程中不易得到时,也可以采用建造者模式封装该对象的创建过程。

Retrofit中的构建者模式

OkHttpClient:这个是整个OkHttp的核心管理类,内部包含了请求调度器(Dispatcher),请求拦截器(interceptors),代理,读写超时时间等各种需要配置的对象。这里不就符合前面提到的建造者模式使用场景提到的那几点吗!

多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时,则可以使用该模式。

这些配置的对象就是构建OkhttpClient的多个部件/零件。配置不同,导致的运行结果也不同。就像connectTimeout配置的超时时间不同,导致请求结果允许超时时间也不同。

产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能,这个时候使用建造者模式非常合适

在对象创建过程中会使用到系统中的一些其他对象,这些对象在产品对象的创建过程中不易得到时,也可以采用建造者模式封装该对象的创建过程。

这里两点可以合并起来看。OkhttpClient的很多配置对象,如CertificatePinner、protocols这些对象在OkhttpClient的创建过程中不易得到,且创建麻烦。我们应该用建造者模式封装该对象的过程。而且用户不应该知道如此多的创建细节,用户只是要创建一个OkhttpClient对象。不然就违反了迪米特法则了。我们来看下OkhttpClient是具体是怎么实现建造者模式

open class OkHttpClient internal constructor(
  builder: Builder
) : Cloneable, Call.Factory, WebSocket.Factory {

  @get:JvmName("dispatcher") val dispatcher: Dispatcher = builder.dispatcher

  @get:JvmName("connectionPool") val connectionPool: ConnectionPool = builder.connectionPool

  /**
   * Returns an immutable list of interceptors that observe the full span of each call: from before
   * the connection is established (if any) until after the response source is selected (either the
   * origin server, cache, or both).
   */
  @get:JvmName("interceptors") val interceptors: List<Interceptor> =
      builder.interceptors.toImmutableList()

  /**
   * Returns an immutable list of interceptors that observe a single network request and response.
   * These interceptors must call [Interceptor.Chain.proceed] exactly once: it is an error for
   * a network interceptor to short-circuit or repeat a network request.
   */
  @get:JvmName("networkInterceptors") val networkInterceptors: List<Interceptor> =
      builder.networkInterceptors.toImmutableList()
...
}

OKhttpClient构造函数持有builder的依赖,然后其成员变量通过构造函数获取的builder对象,获取其相应的配置对象。然后我们在看Builder类。

class Builder constructor() {
  internal var dispatcher: Dispatcher = Dispatcher()
  internal var connectionPool: ConnectionPool = ConnectionPool()
  internal val interceptors: MutableList<Interceptor> = mutableListOf()
  internal val networkInterceptors: MutableList<Interceptor> = mutableListOf()
  internal var eventListenerFactory: EventListener.Factory = EventListener.NONE.asFactory()
  internal var retryOnConnectionFailure = true
  internal var authenticator: Authenticator = Authenticator.NONE
  internal var followRedirects = true
  ....
  internal var certificatePinner: CertificatePinner = CertificatePinner.DEFAULT
  internal var certificateChainCleaner: CertificateChainCleaner? = null
  internal var callTimeout = 0
  internal var connectTimeout = 10_000
  internal var readTimeout = 10_000
  internal var writeTimeout = 10_000
  internal var pingInterval = 0
  internal var routeDatabase: RouteDatabase? = null

/**
 * Sets the dispatcher used to set policy and execute asynchronous requests. Must not be null.
 */
fun dispatcher(dispatcher: Dispatcher) = apply {
  this.dispatcher = dispatcher
}

/**
 * Sets the connection pool used to recycle HTTP and HTTPS connections.
 *
 * If unset, a new connection pool will be used.
 */
fun connectionPool(connectionPool: ConnectionPool) = apply {
  this.connectionPool = connectionPool
}

/**
 * Returns a modifiable list of interceptors that observe the full span of each call: from
 * before the connection is established (if any) until after the response source is selected
 * (either the origin server, cache, or both).
 */
fun interceptors(): MutableList<Interceptor> = interceptors

fun sslSocketFactory(
  sslSocketFactory: SSLSocketFactory,
  trustManager: X509TrustManager
) = apply {
  if (sslSocketFactory != this.sslSocketFactoryOrNull || trustManager != this.x509TrustManagerOrNull) {
    this.routeDatabase = null
  }

  this.sslSocketFactoryOrNull = sslSocketFactory
  this.certificateChainCleaner = CertificateChainCleaner.get(trustManager)
  this.x509TrustManagerOrNull = trustManager
}

fun addInterceptor(interceptor: Interceptor) = apply {
  interceptors += interceptor
}
...

fun build(): OkHttpClient = OkHttpClient(this)
}

  }

这里的Builder类实际上是OkhttpClient的内部类,也就是产品类的内部类。它是一个具体建造者。 Builder类默认的成员变量会对OkhttpClient的创建所需的对象进行一些默认创建初始化,并会提供一些配置方法,让用户也能灵活控制一些OkhttpClient的创建过程。像拦截器的添加,sslSocketFactory方法,他们返回类型都为Builder。然后通过build()方法直接完成OkhttpClient的构建。

总结

建造者模式的优点:

  • 封装性

使用建造者模式可以使客户端不必知道产品内部组成的细节。

  • 建造者独立,容易扩展
  • 便于控制细节风险

建造者模式的使用场景

  • 相同的方法,不同的执行顺序,产生不同的事件结果时,可以采用建造者模式
  • 多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时,则可以使用该模式。
  • 产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能,这个时候使用建造者模式非常合适
  • 在对象创建过程中会使用到系统中的一些其他对象,这些对象在产品对象的创建过程中不易得到时,也可以采用建造者模式封装该对象的创建过程

最后小编在这分享一份,我在学习提升时,从网上收集整理了一些 android 开发相关的学习文档、面试题、Android 核心笔记等等文档,希望能帮助到大家学习提升,如有需要参考的可以直接去我 CodeChina地址:https://codechina.csdn.net/u012165769/Android-T3 访问查阅

以上是关于从Retrofit/Okhttp源码 学习设计模式-建造者模式的主要内容,如果未能解决你的问题,请参考以下文章

retrofit+okhttp源码流程

retrofit+okhttp源码流程

retrofit+okhttp源码流程

Retrofit源码解析

Retrofit源码解析

spring-cloud-square源码速读(retrofit + okhttp篇)