Android : Builder模式 详解及学习使用

在此声明:以下内容由书籍 《Android高级进阶》学习而来。



Builder模式是一种设计模式,最初被介绍于《设计模式:可复用面向对象软件的基础》,目前在Java及Android中用处更是十分广泛,因此基本的了解与学习应当掌握。

一. Builder模式介绍

首先从它的定义开始介绍:

Builder模式:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

一般而言,Builder模式主要由四个部分组成:

  • Product :被构造的复杂对象,ConcreteBuilder 用来创建该对象的内部表示,并定义它的装配过程。
  • Builder :抽象接口,用来定义创建 Product 对象的各个组成部分的组件。
  • ConcreteBuilder : Builder接口的具体实现,可以定义多个,是实际构建Product 对象的地方,同时会提供一个返回 Product 的接口。
  • Director : Builder接口的构造者和使用者。


以代码的形式来进行说明,首先创建Product 类:

public class Product {
    private String partOne;
    private String partTwo;

    public String getPartOne() {
        return partOne;
    }

    public void setPartOne(String partOne) {
        this.partOne = partOne;
    }

    public String getPartTwo() {
        return partTwo;
    }

    public void setPartTwo(String partTwo) {
        this.partTwo = partTwo;
    }
}

创建Builder接口:

public interface Builder {
    public void buildPartOne();
    public void buildPartTwo();
    public void getProduct();
}

创建两个ConcreteBuilder 类,即实现Builder接口:

public class ConcreteBuilderA implements Builder {

    private Product product;

    @Override
    public void buildPartOne() {   
    }

    @Override
    public void buildPartTwo() {
    }

    @Override
    public Product getProduct() {
        return product;
    }
}
public class ConcreteBuilderB implements Builder {

    private Product product;

    @Override
    public void buildPartOne() {   
    }

    @Override
    public void buildPartTwo() {
    }

    @Override
    public Product getProduct() {
        return product;
    }
}

最后创建Director

public class Director {
    private Builder builder;

    public Director(Builder builder){
        this.builder = builder;
    }

    public  void buildProduct(){
        this.builder.buildPartOne();
        this.builder.buildPartTwo();
    }

    public Product getProduct(){
        return this.builder.getProduct();
    }
}





二. Builder模式进化

以上代码只是最基本的展示了Builder模式,基于定义的介绍。这种基本模式重点在于:抽象出对象创建的步骤,并通过调用不同的具体实现从而得到不同的结果。 但是在实际运用中并非是以上形式,而是进化成另一种体现形式,目的在于 减少对象创建过程中引入的多个重载构造函数、可选参数以及setter过度使用导致不必要的复杂性。



下面还是以代码的形式来进行讲解,只是这次更加具体到一个熟悉的对象User,假以它有以下属性,且都是不可变(final)的。【应尽量将属性值定义为不可变】

public class User {
    private final String mName;     //必选
    private final String mGender;   //可选
    private final int mAge;         //可选
    private final String mPhone;    //可选
}

这个 User类中 mName属性是必选的,其余的可选,接下来该如何构造这个实例呢?在此有两个前提:

  • 所有属性值以声明为 final,因此必须在构造函数中对这些属性初始化,否则编译不通过。
  • 可是以上属性值分为必选可选,所以构造函数需要提供不同的参数组合的方法。(即可选参数可以忽略)


方案一:

因此,综上两个前提,最直接的一个方案是定义多个重载的构造函数,其中一个构造函数只接收必选参数,其余的构造函数不仅要接收必选参数,还要接收不同可选参数的组合,代码如下:

public class User {
    private final String mName;     //必选
    private final String mGender;   //可选
    private final int mAge;         //可选
    private final String mPhone;    //可选

    public User(String mName) {
        this(mName, "");
    }

    public User(String mName,String mGender) {
        this(mName, mGender, 0);
    }

    public User(String mName, String mGender, int mAge) {
        this(mName, mGender, mAge, "");
    }

    public User(String mName, String mGender, int mAge, String mPhone) {
        this.mName = mName;
        this.mGender = mGender;
        this.mAge = mAge;
        this.mPhone = mPhone;
    }
}

这种构造函数的方式虽然简单,但是 只适用于少量属性的情况,一旦属性增多,构造函数的数量也会随着线性增长,因此并不好阅读及维护。



方案二:

第二种方案是遵循 JavaBeans 规范,定义一个默认的无参数构造函数,并为类的每个属性都提供getters 和 setters函数,代码如下:

public class User {
    private String mName;     //必选
    private String mGender;   //可选
    private int mAge;         //可选
    private String mPhone;    //可选

    public String getName() {
        return mName;
    }

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

    public String getGender() {
        return mGender;
    }

    public void setGender(String mGender) {
        this.mGender = mGender;
    }

    public int getAge() {
        return mAge;
    }

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

    public String getPhone() {
        return mPhone;
    }

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

这种方案相较于第一种方案的好处是易于阅读和维护,使用者可以创建一个空实例User,并只设置需要的属性值,可是此方案有两个缺点:

  • User类是可变的,禁锢了对象的可变性。
  • User类的实例状态不连续。如果想要创建一个同时具有所有属性的类实例,那么直到第五个属性值的 set函数被调用时,该类实例才具有完整连续的状态。这也就意味着类的调用者可能会看到类实例的不连续状态。


方案三: ☆☆☆☆☆

了解了以上两种比较常见但是效果却不太理想的方案后,正式引出第三种方案 —— 进化后的 Builder模式,既有以上两种方案的优点,又摒弃它们的缺点。代码如下:

public class User {
    private final String mName;     //必选
    private final String mGender;   //可选
    private final int mAge;         //可选
    private final String mPhone;    //可选

    public User(UserBuilder userBuilder) {
        this.mName = userBuilder.name;
        this.mGender = userBuilder.gender;
        this.mAge = userBuilder.age;
        this.mPhone = userBuilder.phone;
    }

    public String getName() {
        return mName;
    }

    public String getGender() {
        return mGender;
    }

    public int getAge() {
        return mAge;
    }

    public String getPhone() {
        return mPhone;
    }


    public static class UserBuilder{
        private final String name;     
        private String gender;   
        private int age;         
        private String phone;

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

        public UserBuilder gender(String gender){
            this.gender = gender;
            return this;
        }

        public UserBuilder age(int age){
            this.age = age;
            return this;
        }

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

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


}

从以上代码可以看出这几点:

  • User类的构造函数是私有的,这意味着调用者不可直接实例化这个类。
  • User类是不可变的,其中必选的属性值都是 final 的并且在构造函数中设置;同时对所有的属性取消 setters函数,只保留 getter函数。
  • UserBuilder 的构造函数只接收必选的属性值作为参数,并且只是将必选的属性设置为 fianl来保证它们在构造函数中设置。

接下来,User类的使用方法如下:

    public User getUser(){
        return new
                User.UserBuilder("gym")
                .gender("female")
                .age(20)
                .phone("12345678900")
                .build();
    }

以上通过 进化的Builder模式形象的体现在User的实例化。






三. AS 中自动生成 变化Builder模式

分析比较了以上三个方案后,也学习了解了 化Builder模式的好处和运用方法,可是你会发现它需要编写很多样板代码,需要在内部类 UserBuilder 中重复外部类User的属性定义。其实在AS中,可以通过安装 InnerBuilder 的插件来简化 Builder模式的创建过程,以下为安装使用步骤:


1. 在File菜单下打开 settings 选项,点开Plugins。

这里写图片描述


2. 在中间的输入框输入 Builder 点击搜索,安装后根据提示重开AS即可。

这里写图片描述


3. 在编写User类时,只需要把属性名确定下来,然后鼠标右键打开Generate菜单,选择 Builder按钮,在弹出的Builder配置对话框中进行相关配置的勾选,如下:

这里写图片描述


完成以上步骤后,即可自动生成 进化Builder模式 代码,可能稍有不同,根据自身需求修改即可。






四. 开源函数库的例子

正如开头所说,Builder模式 被广泛运用于Android中的各个领域,无论是Android SDK 或各种开元函数库中,接下来关于Builder模式的运用举几个例子:


1. Android系统对话框 AlertDialog 的使用

    AlertDialog alertDialog = new AlertDialog.Builder(this).
            setTitle("标题").
            setMessage("内容").
            setIcon(R.drawable.ic_logo).
            create();
    alertDialog.show();

2.网络请求框架 OkHttp 的请求封装:

private Request(Builder builder){
    this.url = builder.url;
    this.method = builder.method ;
    this.headers = builder.headers.build() ;
    this.body = builder.body ;
    this.tag = builder.tag != null ? builder.tag : this;
}

3. 图片缓存函数库 Android-Universal-Image-Loader 的全局配置也运用到了,在此不再举例了。






其实以上内容几乎都是书上的,前几天读到了这一章内容,讲的十分浅显易懂,之前自己没怎么理解这块内容,趁这次机会详细学习了一遍,以上内容及代码均手码出来,包括安装插件,亲自动手实践学习理解更深,在此谢过 顾浩鑫coder。

希望对你有帮助 :)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值