第三章、Builder模式
1.定义
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
2.使用场景
(1)相同的方法,不同的执行顺序,产生不同的事件结果时。
(2)多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时。
(3)产品类非常复杂,或者产品类中的调用顺序不同产生了不同的作用,这个使用建造者模式非常适合。
(4)当初始化一个对象特别复杂时,如参数多,且很多参数有默认值。
3.简单实现
public interface Builder {
//创建部件A 比如创建汽车车轮
void buildPartA();
//创建部件B 比如创建汽车方向盘
void buildPartB();
//创建部件C 比如创建汽车发动机
void buildPartC();
//返回最后组装成品结果 (返回最后装配好的汽车)
Product getResult();
}
//Director 类,负责制造
public class Director {
private Builder builder;
public Director( Builder builder ) {
this.builder = builder;
}
// 将部件partA partB partC最后组成复杂对象
//这里是将车轮 方向盘和发动机组装成汽车的过程
public void construct() {
builder.buildPartA();
builder.buildPartB();
builder.buildPartC();
}
}
public class ConcreteBuilder implements Builder {
Part partA, partB, partC;
public void buildPartA() {
//这里是具体如何构建partA的代码
};
public void buildPartB() {
//这里是具体如何构建partB的代码
};
public void buildPartC() {
//这里是具体如何构建partB的代码
};
public Product getResult() {
//返回最后组装成品结果
};
}
public interface Product { }//产品
public interface Part { }//部件
//调用
ConcreteBuilder builder = new ConcreteBuilder();
Director director = new Director( builder );
director.construct();
Product product = builder.getResult();
从上面可以看到由于Director封装了构建复杂的产品对象过程,对外隐藏了细节。
现实开发中,Director一般被省略。而直接使用一个Builder来进行对象的组装,这个Builder通常为链式调用,他是将setter方法返回自身。代码大致如下:
new TestBuilder().setA("A").setB("B").create();
4.Android源码中的Builder模式
1.AlertDialog.Builder
源码太长截取部分:
public static class Builder {
private final AlertController.AlertParams P;
private int mTheme;
/** * Constructor using a context for this builder and the {@link AlertDialog} it creates. */
public Builder(Context context) {
this(context, resolveDialogTheme(context, 0));
}
/** * Constructor using a context and theme for this builder and * the {@link AlertDialog} it creates. The actual theme * that an AlertDialog uses is a private implementation, however you can * here supply either the name of an attribute in the theme from which * to get the dialog's style (such as {@link android.R.attr#alertDialogTheme} * or one of the constants * {@link AlertDialog#THEME_TRADITIONAL AlertDialog.THEME_TRADITIONAL}, * {@link AlertDialog#THEME_HOLO_DARK AlertDialog.THEME_HOLO_DARK}, or * {@link AlertDialog#THEME_HOLO_LIGHT AlertDialog.THEME_HOLO_LIGHT}. */
public Builder(Context context, int theme) {
P = new AlertController.AlertParams(new ContextThemeWrapper(
context, resolveDialogTheme(context, theme)));
mTheme = theme;
}
/** * Returns a {@link Context} with the appropriate theme for dialogs created by this Builder. * Applications should use this Context for obtaining LayoutInflaters for inflating views * that will be used in the resulting dialogs, as it will cause views to be inflated with * the correct theme. * * @return A Context for built Dialogs. */
public Context getContext() {
return P.mContext;
}
/** * Set the title displayed in the {@link Dialog}. * * @return This Builder object to allow for chaining of calls to set methods */
public Builder setTitle(CharSequence title) {
P.mTitle = title;
return this;//**这里返回自身,类似的来设置各种参数。
}
// ......省略
/** * Creates a {@link AlertDialog} with the arguments supplied to this builder. It does not * {@link Dialog#show()} the dialog. This allows the user to do any extra processing * before displaying the dialog. Use {@link #show()} if you don't have any other processing * to do and want this to be created and displayed. */
public AlertDialog create() {
final AlertDialog dialog = new AlertDialog(P.mContext, mTheme, false);
P.apply(dialog.mAlert);//将P中的参数应用到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;
}
/** * Creates a {@link AlertDialog} with the arguments supplied to this builder and * {@link Dialog#show()}'s the dialog. */
public AlertDialog show() {
AlertDialog dialog = create();
dialog.show();
return dialog;
}
}
上面有个AlertController.AlertParams 的成员参数P,我们在Builder设置的title,icon等都储存在他里面。在调用create时在P.apply使用。
5.总结
优点:
(1)良好的封装性,使用建造者模式可以使客户端不必知道产品内部组成细节。
(2)建造者独立,容易扩展。
缺点:
(1)会产生多余的Builder对象及Director对象,消耗内存。
6.参考
链接:设计模式之Builder