Builder定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
Builder使用场景:
- 相同的方法,不同的执行顺序,产生不同的事件结果时
- 多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时
- 产品类非常复杂,或者产品类中的调用顺序不同产生了不同的作用,这个时候使用建造者模式非常合适
- 当初始化一个对象特别复杂,如参数多,且很多参数都具备默认值时
Builder的简单应用
大家考虑一下这种场景,假如有一个类(Computer),里面有很多属性,就像下面的代码:
//计算机的抽象类,即Product角色
public class Computer {
private String mModel; //型号 (必传参数)
private int mMemory; //内存 (必传参数)
private int mGraphics; //显卡 (不必传参数)
private String mBoard; //主机 (不必传参数)
private String mDisplay; //显示器 (不必传参数)
private String mProcessor; //处理器 (不必传参数)
}
在这个类中,有些参数是必要的,而有些参数是非必要的。就好比在买电脑的时候,只需要知道型号,内存等些重要的信息,其他的信息可以关注也可以不关注的时候,那么问题就来了,如何创建这个类的对象呢?
一种可行的方案就是实用构造方法。第一个构造方法只包含两个必需的参数,第二个构造方法中,增加一个可选参数,第三个构造方法中再增加一个可选参数,依次类推,直到构造方法中包含了所有的参数,也就是构造方法的重构:
public Computer(String mModel, int mMemory) {
this.mModel = mModel;
this.mMemory = mMemory;
}
public Computer(String mModel, int mMemory, int mGraphics) {
this.mModel = mModel;
this.mMemory = mMemory;
this.mGraphics = mGraphics;
}
public Computer(String mModel, int mMemory, int mGraphics, String mBoard) {
this.mModel = mModel;
this.mMemory = mMemory;
this.mGraphics = mGraphics;
this.mBoard = mBoard;
}
public Computer(String mModel, int mMemory, int mGraphics, String mBoard, String mDisplay) {
this.mModel = mModel;
this.mMemory = mMemory;
this.mGraphics = mGraphics;
this.mBoard = mBoard;
this.mDisplay = mDisplay;
}
public Computer(String mModel, int mMemory, int mGraphics, String mBoard, String mDisplay, String mProcessor) {
this.mModel = mModel;
this.mMemory = mMemory;
this.mGraphics = mGraphics;
this.mBoard = mBoard;
this.mDisplay = mDisplay;
this.mProcessor = mProcessor;
}
这种做法可以实现,但是大家试想,如果参数多了怎么办?可读性不好,且代码维护成本高。而且对调用者来说也比较的麻烦,还要传入不需要参数的默认值,搞不好还会出错。
第二种解决办法就出现了,我们同样可以根据JavaBean的习惯,设置一个空参数的构造方法,然后为每一个属性设置setters和getters方法。就像下面一样:
public Computer() {
}
public String getmModel() {
return mModel;
}
public void setmModel(String mModel) {
this.mModel = mModel;
}
public int getmMemory() {
return mMemory;
}
public void setmMemory(int mMemory) {
this.mMemory = mMemory;
}
public int getmGraphics() {
return mGraphics;
}
public void setmGraphics(int mGraphics) {
this.mGraphics = mGraphics;
}
public String getmBoard() {
return mBoard;
}
...
这种方法看起来可读性不错,而且易于维护。作为调用者,创建一个空的对象,然后只需传入我感兴趣的参数。那么缺点呢?也很明显: 对象会产生不一致的状态。当你想要传入6个参数的时候,你必需将所有的setXX方法调用完成之后才行。然而一部分的调用者看到了这个对象后,以为这个对象已经创建完毕,就直接用了,其实User对象并没有创建完成。
说了那么多,就是为了衬托Builder的好处,它处理这种事情最合适不过
//计算机的抽象类,即Product角色
public class Computer {
private final String mModel; //型号 (必传参数)
private final int mMemory; //内存 (必传参数)
private final int mGraphics; //显卡 (不必传参数)
private final String mBoard; //主机 (不必传参数)
private final String mDisplay; //显示器 (不必传参数)
private final String mProcessor; //处理器 (不必传参数)
private Computer(ComputerBuilder builder) {
this.mModel = builder.mModel;
this.mMemory = builder.mMemory;
this.mGraphics = builder.mGraphics;
this.mBoard = builder.mBoard;
this.mDisplay = builder.mDisplay;
this.mProcessor = builder.mProcessor;
}
public String getModel() {
return mModel;
}
public int getMemory() {
return mMemory;
}
public int getGraphics() {
return mGraphics;
}
public String getBoard() {
return mBoard;
}
public String getDisplay() {
return mDisplay;
}
public String getProcessor() {
return mProcessor;
}
public static class ComputerBuilder {
private final String mModel; //型号
private final int mMemory; //内存
private int mGraphics; //显卡
private String mBoard; //主机
private String mDisplay; //显示器
private String mProcessor; //处理器
public ComputerBuilder(String mModel, int mMemory) {
this.mModel = mModel;
this.mMemory = mMemory;
}
public ComputerBuilder graphics(int graphics) {
this.mGraphics = graphics;
return this;
}
public ComputerBuilder board(String board) {
this.mBoard = board;
return this;
}
public ComputerBuilder display(String display) {
this.mDisplay = display;
return this;
}
public ComputerBuilder processor(String processor) {
this.mProcessor = processor;
return this;
}
public Computer build() {
return new Computer(this);
}
}
}
有几个重要的地方需要说明一下:
- Computer类的构造方法是私有的。也就是说调用者不能直接创建Computer对象
- Computer类的属性都是不可变的。所有的属性都添加了final修饰符,并且在构造方法中设置了值。并且,对外只提供getter()方法。
- Builder模式使用了链式调用。可读性更佳。
- Builder的内部类构造方法中只接收必传的参数,并且该必传的参数适用了final修饰符
写了那么久了,现在怎么构建一个对象?so easy
new Computer.ComputerBuilder("ThinkPad x1",8)
.graphics(4)
.board("华硕")
.display("高分屏")
.processor("i7-4600u")
.build();
传说中的链式调用有没有,是不是相当的整洁,一行代码搞定。
builder的总结
优点:
a.良好的封装性,使用建造者模式可以使客户端不必知道产品内部组成的细节
b.建造者独立,容易扩展
缺点
a.会产生多余的Builder对象以及Director对象,消耗内存。
Builder模式的自动化生成
上面的builder虽然非常棒,但是需要写的东西重复性很高,这怎么能是我们该做的事情呢?在Android Studio中,可以通过安装名为InnerBuilder的插件来简化Builder模式的创建过程,在Plugins面板中搜索builder即可找到这个插件,如图:
下载完成后,会重启Android Studio使插件生效。这里以User类代码为例,在编写时,只需把属性名确定下来,然后单击鼠标右键,打开Generate菜单,选择Builder按钮,
点击Builder,在弹出的配置中勾选相关配置,如图:
点击OK,即可自动生成Builder相关代码,生成的是普通的,咱们要根据实际情况进行少量修改即可
public class User {
private final String mFirstName; // 必选
private final String mLastName; // 必选
private final String mGender; // 非必选
private final int mAge; // 非必选
private final String mPhoneNo; // 非必选
private User(Builder builder) {
mFirstName = builder.mFirstName;
mLastName = builder.mLastName;
mGender = builder.mGender;
mAge = builder.mAge;
mPhoneNo = builder.mPhoneNo;
}
public static final class Builder {
private String mFirstName;
private String mLastName;
private String mGender;
private int mAge;
private String mPhoneNo;
public Builder() {
}
public Builder mFirstName(String val) {
mFirstName = val;
return this;
}
public Builder mLastName(String val) {
mLastName = val;
return this;
}
public Builder mGender(String val) {
mGender = val;
return this;
}
public Builder mAge(int val) {
mAge = val;
return this;
}
public Builder mPhoneNo(String val) {
mPhoneNo = val;
return this;
}
public User build() {
return new User(this);
}
}
}
到这里已经将Builder模式说的差不多了,有什么问题可以直接留言给我。