您的位置:首页 > 产品设计 > UI/UE

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

2017-01-27 13:46 302 查看
在此声明:以下内容由书籍 《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。

希望对你有帮助 :)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: