参考
- 《设计模式:可复用面向对象软件的基础 》3.2 Builder 生成器--对象创建型模式
- 《Android源码设计模式解析与实战》第3章 Builder模式
意图
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
适用性
- 相同的方法,不同的执行顺序,产生不同的事件结果时。
- 多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时。
- 产品类非常复杂,或者产品类中的调用顺序不同产生了不同的作用,这个时候使用建造者模式非常合适。
- 当初始化一个对象特别复杂,如参数多,且很多参数都具有默认值时。
结构
- Product
- 表示被构造的复杂对象
- 包含定义组成部件的类,包括将这些部件装配成最终产品的接口
- Builder 抽象Builder类,为创建一个Product对象的各个部件指定抽象接口,规范产品的组建,一般是由子类实现具体的组建过程。
- ConcreteBuilder 具体的Builder实现类
- Director 使用Builder接口进行构造
协作
- 客户创建Director对象,并用它所想要的Builder对象进行配置。
- 一旦产品部件被生成,Builder就会通知生成器
- 生成器处理Builder的请求,并将部件添加到该产品中。
- 客户从生成器中检索产品
例子1:RTF阅读器
一个RTF(Rich Text Format)阅读器应能将RTF转换为多种正文格式。该阅读器可以将RTF文档转换为普通ASCII文本或者一个能以交互方式编辑的正文窗口组件。但问题是在于可能转换的数目是无限的。因此要能够很容易地实现新的转换的增加,同时却不改变RTF阅读器。
一种解决办法就是运用Builder模式,用一个可以将RTF转换成另一种正文表示的TextConverter对象配置这个RTFReader类。当RTFReader对RTF文档进行语法分析时,它使用TextConverter去做转换。
每种转换器类将创建和装配一个复杂对象的机制隐含在抽象接口的后面。转换器独立于阅读器,阅读器负责对一个RTF文档进行语法分析。
每一个转换器类可以作为生成器builder,而阅读器作为director。Builder模式将分析文本格式的算法与描述怎样创建和表示一个转换后格式的算法分离开来。这使得我们可以重用RTFReader的语法分析算法,分局RTF文档创建不同的正文表示--仅需使用不同的TextConverter的子类配置该RTFReader即可。
例子2:组装电脑
我想随意组装一台电脑,比如一台Inter主板,Retina显示屏,MacOs的电脑
计算机的组装过程较为负复杂,并且组装顺序是不固定的,所以我们可以用Buider模式。
示例代码:
// 电脑基础抽象类
public abstract class Computer {
protected String mBoard;
protected String mDisplay;
protected String mOS;
protected Computer(){}
//设置主板
public void setBoard(String board){
this.mBoard=board;
}
//设置显示器
public void setDisplay(String display) {
mDisplay=display;
}
//设置操作系统
public abstract void setOS();
@Override
public String toString() {
StringBuilder stringBuilder=new StringBuilder();
if (mBoard!=null) {
stringBuilder.append("myBoard is:"+mBoard);
}
if (mDisplay!=null) {
stringBuilder.append("\nmDisplay is:"+mDisplay);
}
if (mOS!=null) {
stringBuilder.append("\nmyOS is:"+mOS);
}
return stringBuilder.toString();
}
}
//Macbook电脑,实现了setOs
public class MacBook extends Computer{
@Override
public void setOS() {
mOS="Mac _S X 10.10";
}
}
// 抽象Builder
public abstract class Builder{
abstract Buider buildBoard(String board);
abstract Buider buildDisplay(String display);
abstract Buider buildOS();
abstract Computer create();
}
//具体的一个Builder类
public class MacBookBuilder extends Buider {
private Computer mComputer=new MacBook();
@Override
public Buider buildBoard(String board) {
mComputer.setBoard(board);
return this;
}
@Override
public Buider buildDisplay(String display) {
mComputer.setDisplay(display);
return this;
}
@Override
public Buider buildOS() {
mComputer.setOS();
return this;
}
@Override
public Computer create() {
return mComputer;
}
}
// Director类,负责构造Computer
public class Director{
Builder mBuilder = null;
public Director(Builder builder){
mBuilder = builder;
}
public void construct(String board,String display){
mBuilder.buildBoard(board);
mBuilder.buildDisplay(display);
mBuilder.buildOs();
}
}
//使用
public static void main(String[] args) {
Builder macBookBuilder=new MacBookBuilder();
Director director = new Director(macBookBuilder);
// 使用Director构造
director.construct(英特尔主板,retina);
System.out.println(macBookBuilder.create().toString())
// 通常我们都会通过Builder的链式调用省略Director类,比如如下所示
System.out.println(macBookBuilder.buildBoard("英特尔主板")
.buildDisplay("retina")
.buildOS()
.create().toString());
}
结果:
myBoard is:英特尔主板
mDisplay is:retina
myOS is:Mac _S X 10.10
效果
- 它使你可以改变一个产品的内部表示
- 它将构造代码和表示代码分开
- 它使你可对构造过程进行更精细的控制
- 《Effective java》书中建议如果构造一个对象复杂,需要传入很多参数,不妨采用Builder模式,有效减少了传参失误。
- 当然缺点是增加了代码量。
例子3 Android中的AlertDialog.Builder
// todo 后更