美文网首页android网络开发Android知识Android开发
结合okhttp3的OkHttpClient类谈build设计模

结合okhttp3的OkHttpClient类谈build设计模

作者: 昵称真难选 | 来源:发表于2017-04-29 02:52 被阅读176次

    原文出自王艳涛的专栏转载请注明出处!

    概述

    build模式一般用来一步一步创建一个复杂对象的创建型模式,适用于初始化一个参数比较多且具有默认值的复杂对象。经过阅读下文你会发现okhttp3的OkHttpClient类是比较典型的build模式,所以本文在分析经典的build模式后,再结合okhttp3的OkHttpClient类对build模式进行分析。

    经典的build模式

    经典的build模式基本上包括三个部分,结合下面代码来看:

    1. 待创建对象的抽象基类和具体子类,如Computer类和DellComputer类;
    2. Builder基类和具体子类,如Builder类和DellBuilder类,执行构建,返回构建对象;
    3. Director类,封装构建过程。
    public abstract class Computer {
        protected String name;
        protected String os;
    
        protected Computer() {
        }
    
        public abstract void setName(String name);
        public abstract void setOs(String os);
    }
    
    public class DellComputer extends Computer {
        protected DellComputer() {
            this.name = "dell-0";
            this.os = "os-0";
        }
    
        @Override
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public void setOs(String os) {
            this.os = os;
        }
    }
    
    public abstract class Builder {
        public abstract void name(String name);
        public abstract void os(String os);
        public abstract Computer create();
    }
    
    public class DellBuilder extends Builder{
        private DellComputer dellComputer = new DellComputer();
    
        @Override
        public void name(String name) {
            dellComputer.setName(name);
        }
    
        @Override
        public void os(String os) {
            dellComputer.setOs(os);
        }
    
        @Override
        public Computer create() {
            return dellComputer;
        }
    }
    
    public class Director {
        Builder builder = null;
        public Director(Builder builder){
            this.builder = builder;
        }
        public void construct(String name, String os){
            builder.name(name);
            builder.os(os);
        }
    }
    
    public class Test {
        public static void main(String[] args){
          //构建"dell-1", "os-1"电脑
            Builder builder = new DellBuilder();
            Director director = new Director(builder);
            director.construct("dell-1", "os-1");
            Computer computer = builder.create();
        }
    }
    

    优点:

    具有良好的封装性,使用时不需要了解待构建对象的内部组成细节,同时各子Builder相互独立,容易扩展。

    缺点:

    产生多余的Builder对象和Director对象,消耗内存。

    改进经典的Builder模式第一步——省去Director、实现链式调用

    在实际应用时,一般情况下,Director对象可以省去,直接使用Builder对象进行构建,同时将Builder对象的每个构建方法的返回值设为自身,这样就可以实现链式调用。
    在上述代码的基础上,删除Director类,将Builder类和DellBuilder类修改为如下代码:

    public abstract class Builder {
        public abstract Builder name(String name);//修改返回值为本身
        public abstract Builder os(String os);//修改返回值为本身
        public abstract Computer create();
    }
    
    public class DellBuilder extends Builder{
        private DellComputer dellComputer = new DellComputer();
    
        @Override
        public Builder name(String name) {
            dellComputer.setName(name);
            return this;//返回自身
        }
    
        @Override
        public Builder os(String os) {
            dellComputer.setOs(os);
            return this;//返回自身
        }
    
        @Override
        public Computer create() {
            return dellComputer;
        }
    }
    
    public class Test {
        public static void main(String[] args){
          //构建"dell-1", "os-1"电脑
          Builder builder = new DellBuilder();
          //链式调用
          Computer computer = builder.name("dell-1").os("os-1").create();
        }
    }
    

    改进经典Builder模式第二步——将具体的Builder作为待构建对象的内部类

    将具体的Builder作为待构建对象的内部类可以进一步的减少类的文件数,增强内聚性。
    在第一步改进的基础上,删除Builder类和DellBuilder类,DellComputer类代码改进如下:

    public class DellComputer extends Computer {
        protected DellComputer() {
            this.name = "dell-0";
            this.os = "os-0";
        }
    
        @Override
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public void setOs(String os) {
            this.os = os;
        }
        //Builder作为DellComputer的内部类
        public static class Builder{
          //持有一个DellComputer实例
            Computer computer = new DellComputer();
            public Builder(){}
    
            public Builder name(String name){
                computer.setName(name);
                return this;//返回自身
            }
    
            public Builder os(String os){
                computer.setOs(os);
                return this;//返回自身
            }
    
            public Computer create(){
                return computer;//返回实例
            }
        }
    }
    
    public class Test {
        public static void main(String[] args){
          //构建"dell-1", "os-1"电脑
          //链式调用
          Computer computer = new DellComputer.Builder()
          .name("dell-1").os("os-1").create();
        }
    }
    

    okhttp3中OkhttpClient类的Builder模式

    okhttp3中OkhttpClient类的Builder模式基本上与第二步改进后的Builder模式类似,唯一不同的是OkhttpClient类和其中的Builder内部类都分别增加了一个构造方法,可以相互以对方为参数初始化自己,语言表述有点绕,一看代码相信很快就理解了。
    OkhttpClient类的代码量比较大,但是大部分都是属性和赋值方法,在这里只保留其中一个属性connectTimeout和其赋值方法,将其他的删除,不影响分析其Builder设计模式,整理后的OkhttpClient类代码如下所示(保留关键代码)。

    public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
      //属性
      final int connectTimeout;
      //向外暴露的构造函数
      public OkHttpClient() {
        this(new Builder());//返回Builder实例
      }
      //用Builder实例初始化OkHttpClient属性
      OkHttpClient(Builder builder) {
        this.connectTimeout = builder.connectTimeout;
      }
      //为属性赋值
      public int connectTimeoutMillis() {
        return connectTimeout;
      }
      //返回一个经过OkHttpClient实例初始化后的Builder实例
      public Builder newBuilder() {
        return new Builder(this);
      }
      //内部类Builder
      public static final class Builder {
        int connectTimeout;
        //构造函数
        public Builder() {
          connectTimeout = 10_000;
        }
        //通过OkHttpClient实例初始化自己
        Builder(OkHttpClient实例初始化自己 okHttpClient) {
          this.connectTimeout = okHttpClient.connectTimeout;
        }
        //构建方法
        public Builder connectTimeout(long timeout, TimeUnit unit) {
          connectTimeout = checkDuration("timeout", timeout, unit);
          return this;
        }
        //启动构建,返回经过自己初始化后的OkHttpClient实例
        public OkHttpClient build() {
          return new OkHttpClient(this);
        }
      }
    }
    

    获取OkHttpClient实例的用法:

    分析上述代码,就可以得出开发时获取OkHttpClient实例的用法,分为两种情况:

    1. 使用默认配置,不修改OkHttpClient的属性时,获取默认OkHttpClient实例代码如下:
    2. 需要自定义配置,获取配置后的OkHttpClient实例。

    第一种情况下,获取默认OkHttpClient实例代码如下,在调用构造函数时,用Builder的默认属性值初始化自己。

    其中方法1是通过暴露能够根据Builder初始化自己的构造函数实现的,是okhttp官方给出的便捷获取默认OkHttpClient实例的方法。

    方法2可以理解为标准的Builder模式获取OkHttpClient实例的方法。

    但是既然有便捷方法1,所以在获取默认OkHttpClient实例时还是推荐使用方法1。

    OkHttpClient okHttpClient = new OkHttpClient();//方法1
    //或者使用下面的方法
    OkHttpClient okHttpClient = new OkHttpClient.Builder().build;//方法2
    

    第二种情况下,类似第一种情况下的方法2。

    OkHttpClient okHttpClient = new OkHttpClient.Builder()
    .connectTimeout(1000, TimeUnit.SECONDS).build;
    

    总结

    本文从经典的Builder模式入手,分析了一般情况下Builder的改进实现方式,同时又结合了okhttp3中OkHttpClient类,对Builder模式的实现进一步分析。
    总之,如果一个类的属性特别多,有默认值,构造时特别麻烦,那么别犹豫,Builder模式当仁不让。同时根据项目自身的情况,决定采取哪一种形式的Builder模式,是经典模式还是改进一或者改进二,再或者使用OkHttpClient类的方法,再或者基于此的其他形式,毕竟灵活、方便、解耦、方便阅读才是使用设计模式的最终目的,不必特别拘泥于形式。

    相关文章

      网友评论

        本文标题:结合okhttp3的OkHttpClient类谈build设计模

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