一、定义
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
二、使用场景
- 当初始化一个有很多参数的对象,且很多参数都有默认值。
- 多个部件或零件,都可以装配到同一个对象中,但是产生的运行结果又不同。
- 相同的方法,不同的执行顺序,产生不同的事件结果。
三、经典Builder模式实现
1. 抽象对象
public abstract class Computer {
protected String mBoard;
protected String mDisplay;
protected String mOS;
protected Computer() {
}
public void setBoard(String board) {
mBoard = board;
}
public void setDisplay(String display) {
mDisplay = display;
}
public abstract void setOS();
@Override
public String toString() {
return "Computer{" +
"mBoard='" + mBoard + '\'' +
", mDisplay='" + mDisplay + '\'' +
", mOS='" + mOS + '\'' +
'}';
}
}
2. 具体需要被构建的对象
public class MacBook extends Computer {
public MacBook() {
}
@Override
public void setOS() {
mOS = "Mac OS";
}
}
3. 构建者抽象类
public abstract class Builder {
public abstract void buildBoard(String board);
public abstract void buildDisplay(String display);
public abstract void buildOS();
public abstract Computer create();
}
4. 具体对象的构建者
实现抽象Builder类。
public class MacBookBuilder extends Builder {
private Computer mComputer = new MacBook();
@Override
public void buildBoard(String board) {
mComputer.setBoard(board);
}
@Override
public void buildDisplay(String display) {
mComputer.setDisplay(display);
}
@Override
public void buildOS() {
mComputer.setOS();
}
@Override
public Computer create() {
return mComputer;
}
}
5. 负责构建的Director
public class Director {
private Builder mBuilder;
public Director(Builder builder) {
mBuilder = builder;
}
public void construct(String board, String display) {
mBuilder.buildBoard(board);
mBuilder.buildDisplay(display);
mBuilder.buildOS();
}
}
6. 使用
public class Test {
private static final String TAG = "Test";
public static void test() {
MacBookBuilder builder = new MacBookBuilder();
new Director(builder).construct("Intel", "Retina");
Log.d(TAG, "test: " + builder.create().toString());
}
}
这是GOF设计模式中Builer模式的简单实现,通过MacBookBuilder来构建MacBook,Director进一步封装构建细节。Builder和Director一起将一个复杂对象的构建与其表示分离。
四、AlertDialog中的Builder模式
上面说的是GOF设计模式中Builder模式的简单实现,我会感觉到Direcor和Builder一起用有点啰嗦。今天上课时老师也说到了这本经典的书中的有些内容在现在已经不那么用了,大概Builder模式就是这样吧。
以AlertDialog类为例看看Android中的Builder模式是如何设计的。
1. Director的移除
Android中很多地方都用到了Builder模式,看过源码的就会发现很多的Builder模式中并没有Director类,没有去封装构建的具体流程,而是直接使用Builder提供的方法。
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setIcon(R.drawable.ic_launcher_background);
builder.setTitle("title");
builder.setNegativeButton("n", (dialog, which) -> Log.d(TAG, "onClick: "));
AlertDialog dialog = builder.create();
// 其实Builder也封装了show()方法,可以直接调用Builder对象的show()来显示
dialog.show();
2. 结合链式调用
上面的写法,不停地调用builder的方法,其实也不是我们平常使用的写法。构建AlertDialog可以一步一步链式调用去显式构造对象。
new AlertDialog.Builder(this)
.setIcon(R.drawable.icon)
.setNegativeButton("n", (dialog, which) -> Log.d(TAG, "onClick: "))
.setTitle("title")
// ......
.create()
.show();
实现链式调用的关键就在于Builder中的参数设置方法返回自身Builder对象。
public Builder setIcon(@DrawableRes int iconId) {
P.mIconId = iconId;
return this;
}
3. Builder作为构建对象的内部类
在上面的例子中,MacBook和MacBookBuilder我写成了两个类。MacBookBuilder类就是来管理MacBook中的参数设置的,完全可以作为MacBook的一个静态内部类,这样还好看好找一些,也很符合实际。
从上面AlertDialog的用法中也能看出来AlertDialog的Builder就是其静态内部类。
4. 参数过多时单独封装成参数类
在AlertDialog的Builder中,set方法都是对P的属性去赋值。
public Builder setIcon(@DrawableRes int iconId) {
P.mIconId = iconId;
return this;
}
P其实就是一个参数封装类。AlertParams是AlertController的内部类,保存了一个Dialog对象的所有参数和资源。
private final AlertController.AlertParams P;
public static class AlertParams {
public final Context mContext;
public final LayoutInflater mInflater;
// ...
public CharSequence mTitle;
public View mCustomTitleView;
public CharSequence mMessage;
public CharSequence mPositiveButtonText;
public Drawable mPositiveButtonIcon;
// ...
封装成AlertParams的参数会在Builder的create()中调用apply()设置给Dialog。
public AlertDialog create() {
// ...
P.apply(dialog.mAlert);
// ...
return dialog;
}
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));
}
}
// ......
}
在AlertDialog的设计中,简化了经典的Builder模式,当模块比较稳定时,不存在一些变化时,可以在经典模式上进行一些精简,使这个模式更易于使用。
五、小结
- Builder通常作为配置类的构建器,将构建和表示分离开。
- 也是将配置从目标类中隔离出来,避免过多的setter方法。
- Builder模式通常结合链式调用来实现更简洁易懂的代码。
优点
- 良好的封装性,用户不必知道对象内部组成细节。
- Builder独立,容易扩展。
缺点
- 产生多余的Builder对象、Director对象,消耗内存。