设计模式-1-Builder

读者盆友早上好。从今天开始,我们系列化的介绍设计模式。

先打响设计模式第一枪:Builder模式。笔者的博客目的在于通过实战、实际的例子介绍设计模式,而不是空洞的介绍理论,看了也不怎么清楚到底怎么用。

文章目录

  • 一、典型用法
  • 二、优点1:初始化大量默认参数
  • 三、优点2:流式API、具名参数
  • 四、自己写Builder的思路
  • 五、总结

Builder模式即建造者模式,在Gof这本书中介绍的还是有点晦涩,虽然例子也简单,但是总感觉缺点接地气的感觉。一句话,还是高度抽象了。

学习方法:个人建议《Design Patterns》和《Effective Java》的第二条+《大话设计模式》结合起来理解。

结论:

典型示例用法:

自测接口一般用Postman,如果要在代码中发送请求呢?有很多方法,OkHttpClient就可以实现在代码中发送各种请求,它就是用到Builder模式的典型之一。


//获取默认客户端对象
 private static OkHttpClient getDefaultHttpClient() {
        OkHttpClient ok = new OkHttpClient();
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        ConnectionPool connectionPool = new ConnectionPool(16, 5, TimeUnit.MINUTES);
        builder.connectionPool(connectionPool);
        Dispatcher dispatcher = new Dispatcher();
        dispatcher.setMaxRequests(64);
        dispatcher.setMaxRequestsPerHost(16);
        builder.dispatcher(dispatcher);
        builder.connectTimeout(3, TimeUnit.SECONDS);
        builder.readTimeout(0, TimeUnit.SECONDS);
        builder.writeTimeout(3, TimeUnit.SECONDS);
        return builder.build();
    }
    
  private static OkHttpClient getDefaultHttpClientV2() {
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        Dispatcher dispatcher = new Dispatcher();
        dispatcher.setMaxRequests(64);
        dispatcher.setMaxRequestsPerHost(16);
        return builder.connectionPool(new ConnectionPool(16, 5, TimeUnit.MINUTES))
                .dispatcher(dispatcher).connectTimeout(3, TimeUnit.SECONDS).readTimeout(0, TimeUnit.SECONDS).writeTimeout(3, TimeUnit.SECONDS).build();
    }


//发送一个请求
            Response response = httpClient.newCall(request).execute();


以上就是典型的Builder模式。同样Request、Header都是使用这个模式。

好处:

1、流式API
2、具名的可选参数
3、构造与表示分离

这么说还是抽象,其实《Effective java》的第二条把优点很清楚了,这里不再赘述。
我们今天用OkHttpClient来举例。

一、典型用法


//1.Builder本质是一个静态内部类

public final class OkHttpClient implements Cloneable, Call.Factory {
    ...

  public OkHttpClient() {
    this(new Builder());
  }

  private OkHttpClient(Builder builder) {...}


  public static final class Builder {
  
    public Builder() {
      dispatcher = new Dispatcher();
      protocols = DEFAULT_PROTOCOLS;
      connectionSpecs = DEFAULT_CONNECTION_SPECS;
      proxySelector = ProxySelector.getDefault();
      cookieJar = CookieJar.NO_COOKIES;
      socketFactory = SocketFactory.getDefault();
      hostnameVerifier = OkHostnameVerifier.INSTANCE;
      certificatePinner = CertificatePinner.DEFAULT;
      proxyAuthenticator = Authenticator.NONE;
      authenticator = Authenticator.NONE;
      connectionPool = new ConnectionPool();
      dns = Dns.SYSTEM;
      followSslRedirects = true;
      followRedirects = true;
      retryOnConnectionFailure = true;
      connectTimeout = 10_000;
      readTimeout = 10_000;
      writeTimeout = 10_000;
    }
      public Builder connectTimeout(long timeout, TimeUnit unit) {...}
      
      public Builder readTimeout(long timeout, TimeUnit unit) {...}
       
      public Builder writeTimeout(long timeout, TimeUnit unit) {...}

      
  
}



}


这就是一个Builder模式非常典型的写法。
核心有以下几点:

  • 主体类OkHttpClient的构造方法私有化,提供对外的构造方法是调用的Builder构造器。也就是说Builder构造器是实例化该类的唯一方式
  • 静态内部类Builder是一个静态的、内部类
  • 赋值方法和我们平时用的set()方法很像,只是返回的是Builder而不是void(便于流式API)

二、优点1:初始化大量默认参数

看一下OKOkHttpClient

初始化大量参数用构造方法一般这么做:

Person p = new Person();
p.setHead("Head");
p.setArm("Arm");
p.setBody("Body");
....

这么做存在的问题是什么?
1、你需要知道要设置哪些参数,如果不知道就会遗漏
2、一大推的set,写起来就是一坨“样本代码”

OkHttpClient就有大量默认参数需要初始化,人家默认帮你初始化好,你创建你的Builder即可,保证你不会遗漏关键属性,同时又专注你所要设置的属性。

 public Builder() {
      dispatcher = new Dispatcher();
      protocols = DEFAULT_PROTOCOLS;
      connectionSpecs = DEFAULT_CONNECTION_SPECS;
      proxySelector = ProxySelector.getDefault();
      cookieJar = CookieJar.NO_COOKIES;
      socketFactory = SocketFactory.getDefault();
      hostnameVerifier = OkHostnameVerifier.INSTANCE;
      certificatePinner = CertificatePinner.DEFAULT;
      proxyAuthenticator = Authenticator.NONE;
      authenticator = Authenticator.NONE;
      connectionPool = new ConnectionPool();
      dns = Dns.SYSTEM;
      followSslRedirects = true;
      followRedirects = true;
      retryOnConnectionFailure = true;
      connectTimeout = 10_000;
      readTimeout = 10_000;
      writeTimeout = 10_000;
    }

以上就是OkHttpClient的Builder默认初始化的属性,想一想,如果让你一个一个set的话,真是无聊透顶,而且你还得学习所有的属性,以便都理解后才能赋值。

三、优点2:流式API、具名参数

为避免用户需要知道很多无需知道的细节,现在很简单,我们只用这么做即可。

return builder.connectionPool(new ConnectionPool(16, 5, TimeUnit.MINUTES))
                .dispatcher(dispatcher)
                .connectTimeout(3, TimeUnit.SECONDS)
                .readTimeout(0, TimeUnit.SECONDS)
                .writeTimeout(3, TimeUnit.SECONDS).build();

代码非常简洁,这就是流式API。和java8中的Stream写法类似。

        teamList.stream()
        .filter(e->e.getIsLeader==true)
        .map(TeamListDTO::getId)
        .collect(toList());

这里的具名参数是相对“重叠构造器”(telescoping constructor)而言.如果属性不多,用带不同参数的构造器实例化很好,多了就非常不方便,而且属性有先后顺序且属性名只有类型,赋值容易错。
《Effective Java》第二条说的非常清晰了,不在赘述。

四、自己写Builder的思路

OkHttpClient中的Request、Header都是类似的用法
如果我们自己写,就这么做


//第1步:定义一个final 的class类
类似这样

public final class Request {
}


//第2步:把构造方法私有化,对外提供一个获取Builder的公有方法
类似这样:
  private Request(Builder builder) {}
  
   public Builder newBuilder() {
    return new Builder(this);
  }
  
 好处:外部只能通过Builder来获取一个实例,怎么获取呢?
 
//第3步:构造静态内部类Builder
类似这样:


  public static class Builder {
  
  
}

//第4步:静态内部类Builder的构造方法
public Builder() {
    //无参构造方法初始化各种属性
}

    private Builder(Request request) {
    //私有化有参构造方法,让你不能通过参数实例化
}

//第5步:类似set方法,构造可以流式赋值的返回builder的各个参数的赋值方法
类似这样:
public Builder url(HttpUrl url) {
      if (url == null) throw new IllegalArgumentException("url == null");
      this.url = url;
      return this;
    }
    

 public Builder header(String name, String value) {
      headers.set(name, value);
      return this;
    }
    

五、总结

这个模式个人觉得实际开发中需要自己写的场景挺少的,只要知道是这种设计模式,怎么用、大体优缺点即可。

你可能感兴趣的:(设计模式)