设计模式之Builder模式

  Builder设计模式在代码设计中很常见,譬如我们在阅读大神们的源码是,一些初始化参数比较多构造方法都采用了Builder模式,譬如说Okhttp,Glide,Picasso

Builder模式长什么样?

  举个简单的例子,现在大多数Android应用的开发网络层大多数都在使用OkHttp,熟悉的人都知道,在使用Okhttp之前要进行一些初始化工作,譬如说超时时间缓存策略等等,下面是个简单的初始化例子:

 public static OkHttpClient get() {
        if (okHttpClient == null) {
            synchronized (AppHttpClient.class) {
                if (okHttpClient == null) {
                    okHttpClient = new OkHttpClient.Builder()
                            //cookie保存
                            .cookieJar(new CookiesManager(new PersistentCookieStore(BaseApplication.getInstance())))
                            .sslSocketFactory(createSSLSocketFactory())
                            //支持SSL
                            .hostnameVerifier(new HostnameVerifier() {
                                                  @Override
                                                  public boolean verify(String hostname, SSLSession session) {
                                                      return true;
                                                  }
                                              }
                            )
                            //链接15S超时
                            .connectTimeout(15, TimeUnit.SECONDS)
                            //Read请求30S超时
                            .readTimeout(30, TimeUnit.SECONDS)
                            //Write请求30S超时
                            .writeTimeout(30, TimeUnit.SECONDS)
                            //增加Header
                            .addInterceptor(new HeaderInfoInterceptor())
                            //build
                            .build();
                }
            }
        }
        return okHttpClient;
    }

上面的链式调用方法进行初始化使用的就是Builder设计模式,是不是觉得很方便?

使用场景

  Builder可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。这句话看起来很拗口,不好理解,下边我们举一个简单的例子:

我们在实际开发过程当中,时常会遇到这样一个情况,需要构建一个复杂(属性N多)的对象,像这样婶儿的:

public class Person {
    private String name;        //必须
    private int age;            //必须
    private int sex;            //必须
    private float height;
    private String phone;
    private String address;
    private String email;
}

想要new一个这样的类的实例,于是我们就开始撸下边的代码,通过构造函数的参数的方式去new一个对象:

public Person(String name) {
        this.name = name;
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Person(String name, int age, float height) {
        this.name = name;
        this.age = age;
        this.height = height;
    }

    public Person(String name, int age, float height, int sex) {
        this.name = name;
        this.age = age;
        this.height = height;
        this.sex = sex;
    }

    public Person(String name, int age, float height, int sex, String phone) {
        this.name = name;
        this.age = age;
        this.height = height;
        this.sex = sex;
        this.phone = phone;
    }

    public Person(String name, int age, float height, int sex, String phone, String address) {
        this.name = name;
        this.age = age;
        this.height = height;
        this.sex = sex;
        this.phone = phone;
        this.address = address;
    }

    public Person(String name, int age, float height, int sex, String phone, String address, String email) {
        this.name = name;
        this.age = age;
        this.height = height;
        this.sex = sex;
        this.phone = phone;
        this.address = address;
        this.email = email;
    }

或者使用getter和setter的方式去写一个实现类:

public class Person {
    private String name;        //必须
    private int age;            //必须
    private int sex;            //必须
    private float height;
    private String phone;
    private String address;
    private String email;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getSex() {
        return sex;
    }

    public void setSex(int sex) {
        this.sex = sex;
    }

    public float getHeight() {
        return height;
    }

    public void setHeight(float height) {
        this.height = height;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

先说说这两种方式的优劣:

第一种在参数不多的情况下,是比较方便快捷的,一旦参数多了,代码可读性大大降低,并且难以维护,对调用者来说也造成一定困惑;

第二种可读性不错,也易于维护,但是这样子做对象会产生不一致的状态,当你想要传入全部参数的时候,你必需将所有的setXX方法调用完成之后才行。然而一部分的调用者看到了这个对象后,以为这个对象已经创建完毕,就直接使用了,其实Person对象并没有创建完成,另外,这个Person对象也是可变的,不可变类所有好处都不复存在。

所以有没有更好地方式去实现它呢,那就是接下来要理解的Builder模式了。

以下介绍的Builder模式其实是Builder模式的衍生模式,他与最经典Builder模式有区别,因为现在很少使用经典Builder模式,基本都在使用Builder衍生模式(链式调用),所以经典Builder模式本人也没怎么研究,有兴趣的童鞋可以自行研究下设计模式之四 --- 建造(Builder)模式

新撸出来的代码:

public class Person {
    private final String name;        //必须
    private final int age;            //必须
    private final int sex;            //必须
    private final float height;
    private final String phone;
    private final String address;
    private final String email;


    private Person(PersonBuilder builder){
        this.name = builder.name;
        this.age = builder.age;
        this.sex = builder.sex;
        this.height = builder.height;
        this.phone = builder.phone;
        this.address = builder.address;
        this.email = builder.email;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public int getSex() {
        return sex;
    }

    public float getHeight() {
        return height;
    }

    public String getPhone() {
        return phone;
    }

    public String getAddress() {
        return address;
    }

    public String getEmail() {
        return email;
    }


    public static class PersonBuilder{
        private final String name;        //必须
        private final int age;            //必须
        private final int sex;            //必须
        private  float height;
        private  String phone;
        private  String address;
        private  String email;

        public PersonBuilder(String name, int age, int sex) {
            this.name = name;
            this.age = age;
            this.sex = sex;
        }

        public PersonBuilder height(float height){
            this.height = height;
            return this;
        }

        public PersonBuilder phone(String phone){
            this.phone = phone;
            return this;
        }

        public PersonBuilder address(String address){
            this.address = address;
            return this;
        }

        public PersonBuilder email(String email){
            this.email = email;
            return this;
        }

        public Person build(){
            return new Person(this);
        }
    }
}

从上面的代码中我们可以看到,我们在PersonBuilder类里定义了一份与Person一模一样的变量,通过一系列的成员函数进行设置属性值,但是返回值都是this,也就是都是PersonBuilder对象,最后提供了一个build函数用于创建Person对象,返回的是Person对象,对应的构造函数在Person类中进行定义,也就是构造函数的入参PersonBuilder对象,然后依次对自己的成员变量进行赋值,对应的值都是PersonBuilder对象中的值。此外PersonBuilder类中的成员函数返回Builder对象自身的另一个作用就是让它支持链式调用,使代码可读性大大增强。

Builder模式需要有几个注意的点:

  1. Person类的构造方法必须是私有的,调用者不能直接创建Person对象
  2. Person类的属性是不可变的,所有的属性都要添加final修饰符,并且在构造方法中设置了值,并且对外只提供getters方法。
  3. PersonBuilder的内部类构造方法中只接收必传的参数,并且必传的参数使用final修饰符。

调用方式

new Person.PersonBuilder("张三", 18, 1)
                .address("北京市XXX")
                .email("[email protected]")
                .height(175.5f)
                .phone("1234556")
                .build();

相比起前面通过构造函数和setter/getter方法两种方式,可读性更强。唯一可能存在的问题就是会产生多余的Builder对象,消耗内存。然而大多数情况下我们的Builder内部类使用的是静态修饰的(static),所以这个问题也没多大关系。

怎么样Builder设计模式会使你的代码看上去高大上一些呢?

Android Studio对Builder模式的支持

AS 对Builder这种常用的设计模式提供了支持,方便我们的代码编写,在Plugins中搜索Builder,会出现一个InnerBuilder的插件,

1.jpeg

在编写好类和属性之后,右键generate选择Builder,选择属性

2.jpeg

确定之后就会生成Builder模式下的代码,当然生成的代码和我们希望的还是有些不同,只需要进行小小的修改就可以了,还是很方便的!

总结

优点

  1. 良好的封装性, 使用建造者模式可以使客户端不必知道产品内部组成的细节;
  2. 建造者独立,容易扩展;

缺点

  1. 会产生多余的Builder对象,消耗内存;
  2. 对象的构建过程暴露。

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