设计模式-建造者模式
定义
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
适用场景
- 产品具有复杂的内部结构,这些产品对象具备共性
- 产品的创建需要遵从一定的组建过程
UML类图
设计模式-建造者模式.png- Product: 产品类
- Director: 管理类,用于统一产品构建流程
- Builder:抽象Builder类,规范产品的组建,一般是由子类实现。
- ConcreteBulider: 抽象Builder类的实现类,具体产品每个环节的具体实现,并且提供返回组建好产品
示例
这里以组装电脑为例,电脑由主板,cpu和内存组成。各个厂家组装电脑步骤相同先组装主板,再组装cpu,再组装内存。但不同厂家每个组装具体存在差异。
- 创建产品类
电脑抽象为Computor类
public class Computer {
/**
* 电脑的组成
*/
private String board;
private String cpu;
private String ram;
public void setBoard(String board) {
this.board = board;
}
public void setCpu(String cpu) {
this.cpu = cpu;
}
public void setRam(String ram) {
this.ram = ram;
}
@Override
public String toString() {
return "Computer{" +
"board='" + board + '\'' +
", cpu='" + cpu + '\'' +
", ram='" + ram + '\'' +
'}';
}
}
- 规范产品的组建并可返回产品对象-抽象Builder类
各个电脑厂家组装电脑过程相同,可将其抽象为Builder类
public abstract class Builder {
public abstract void buildBoard(String mainboard);
public abstract void buildCpu(String cpu);
public abstract void buildRam(String ram);
public abstract Computer create();
}
- 具体产品组建-具体的Builder类
具体的电脑厂家例如Lenovo厂(笔者笔记本厂商),产品的每一步都自己进行了实现
public class LenovoComputorBuilder extends Builder {
private Computer mComputer = new Computer();
@Override
public void buildBoard(String board) {
mComputer.setBoard(board);
}
@Override
public void buildCpu(String cpu) {
mComputer.setCpu(cpu);
}
@Override
public void buildRam(String ram) {
mComputer.setRam(ram);
}
@Override
public Computer create() {
return mComputer;
}
}
- 统一组装过程-Director
电脑组装过程标准管理者,每个电脑厂家都要遵守
public class Director {
Builder mBuilder;
public Director(Builder builder) {
this.mBuilder = builder;
}
public void construct(String board, String cpu, String ram) {
// 规范建造流程
// 1. 安装board
// 2. 安装cpu
// 3. 安装ram
mBuilder.buildBoard(board);
mBuilder.buildCpu(cpu);
mBuilder.buildRam(ram);
}
}
- 测试类
public class Test {
public static void main(String[] args) {
Builder builder = new LenovoComputorBuilder();
Director director = new Director(builder);
// 组装电脑
director.construct("80RQ", "i7-6700HQ", "Samsung");
// 提取电脑
Computer computer = builder.create();
System.out.println(computer);
}
}
输出结果
Computer{board='80RQ', cpu='i7-6700HQ', ram='Samsung'}
上面例子中通过LenovoComputorBuilder 构建Lenovo Computor,Director封装了构建Computor的具体过程,对外隐藏了构建细节。Builder和Director一起将一个复杂对象的构建与它的表示分离,使得同样的构建可创建不同的对象。 假如华硕电脑厂商来组装电脑,其组装过程和Lenovo是相同的,但其具体的每个步骤的组装存在差异,便可创建ASUSComputorBuilder对象制定自己的产品组建规范,最终通过Director传入其builder对象,依然可组装处ASUS的Computor。
实际开发中可能对产品的组建过程并不关心,例如弹出一个Dialog,这个Dialog先设置标题还是先设置Button都没有关系,便可省略Director角色。而是直接通过Builder来进行对象的构建,这个Builder常称为链式调用,关键点在于每个setter方法都返回自身,链式调用代码大致如下:
new ConcreteBuilder().setA("A")
.setB("B")
.setC("C")
.create();
优缺点
- 优点
- 易于解耦
将产品本身与产品创建过程进行解耦,可以使用相同的创建过程来得到不同的产品。也就说细节依赖抽象。 - 易于精确控制对象的创建
将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰 - 易于拓展
增加新的具体建造者无需修改原有类库的代码,易于拓展,符合“开闭原则“。每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者。
- 易于解耦
- 缺点
产品的内部构建复杂(例如Lenovo和ASUS,还有各种品牌),可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大
Android源码中的Builder模式
最熟悉的就是AlertDialog.Builder。构建AlertDialog(产品类)示例如下
// 1. 创建AlertDialog的Builder
AlertDialog.Builder builder = new AlertDialog.Builder(this);
// 2. 链式调用设置构建细节
builder.setTitle("builder")
.setIcon(R.drawable.ic_launcher_background)
.setMessage("content");
// 3. 调用builder的create返回产品对象AlertDialog
AlertDialog dialog = builder.create();
// 4. 显示AlertDialog
dialog.show();
AlerDialog.Builder采用的是链式调用,省略了Director管理类,我们看下其源码实现。
AlertDialog$Builder
public static class Builder {
private final AlertController.AlertParams P;
// 将参数设置到AlertController.AlertParms的成员中
public Builder setTitle(CharSequence title) {
P.mTitle = title;
return this;
}
public Builder setMessage(CharSequence message) {
P.mMessage = message;
return this;
}
public Builder setIcon(Drawable icon) {
P.mIcon = icon;
return this;
}
// create返回AlertDialog对象
public AlertDialog create() {
// Context has already been wrapped with the appropriate theme.
final AlertDialog dialog = new AlertDialog(P.mContext, 0, false);
P.apply(dialog.mAlert);
dialog.setCancelable(P.mCancelable);
if (P.mCancelable) {
dialog.setCanceledOnTouchOutside(true);
}
dialog.setOnCancelListener(P.mOnCancelListener);
dialog.setOnDismissListener(P.mOnDismissListener);
if (P.mOnKeyListener != null) {
dialog.setOnKeyListener(P.mOnKeyListener);
}
return dialog;
}
...
}
create方法返回了AlertDialog对象,具体参数设置是在P.apply(dialog.mAlert);
代码中,mAlert就是AlertController对象。
AlertController$AlertParams
public static class AlertParams {
public void apply(AlertController dialog) {
if (mCustomTitleView != null) {
dialog.setCustomTitle(mCustomTitleView);
} else {
if (mTitle != null) {
dialog.setTitle(mTitle);
}
if (mIcon != null) {
dialog.setIcon(mIcon);
}
if (mIconId != 0) {
dialog.setIcon(mIconId);
}
if (mIconAttrId != 0) {
dialog.setIcon(dialog.getIconAttributeResId(mIconAttrId));
}
}
...
}
}
apply方法将所有参数设置到了AlertController,AlertDialog的所有产品参数都是交由AlertController,这样一个具体AlertDialog产品类就创造出来了。
参考
- 建造者模式(Builder Pattern)- 最易懂的设计模式解析
- <<Android源码设计模式>>