0. 序言
- 建造者模式的定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
1. 介绍
- 建造者模式是一步一步创建一个复杂对象的创建型模式,允许在不知道内部构建细节的情况下,可以更精细地控制对象的构造流程。也就是它注重的是构建对象的构成,而不是构件对象过程中所需要的部件表示的细节。
- 举例:贾跃亭会计要造一台FF,而一台FF需要轮胎、方向盘、发动机等部件。而贾跃亭关心的是如何组装这些零件为一辆车,而不是关心这个轮胎怎么造的,这个方向盘怎么造的。
- 优点就是构建过程和部件可以自由扩展,两者之间的耦合降到最低。
2. 场景
- 相同的方法,不同的执行顺序,产生不同的结果。
举例:街舞大家都看过,有时候是动手、动脚、转身,有时候是动脚、转身、动手等等,不同的行动方式,呈现不同的美。 - 初始化一个对象特别复杂,如参数多。
举例:初始化一个AlertDialog或者一个notification,有的需要标题、内容两项,有的需要标题、内容、进度条三项等等,这个时候如果通过构造方法初始化参数的话,会有很多种搭配组合,就要写很多构造方法,这个时候我们就可以使用建造者模式,需要哪个参数我们就拿每个参数对应的方法就行。
3. UML类图(用PC观看)

角色介绍:
① Product产品类——产品的抽象类。
② Builder——抽象Builder类,规范产品的组件。
③ ConcreteBuilder——具体的Builder类,实现具体的组件过程。
④ Director——统一组装过程
4. 场景一实现示例
以跳舞为例:
- 定义Dance抽象类,即Product角色:
public abstract class Dance {
protected abstract void hand();
protected abstract void foot();
protected abstract void turn_around();
}
- 定义Hiphop类,即具体的Dance类:
public class Hiphop extends Dance {
private List<String> mPerformList = new ArrayList<>();
@Override
protected void hand() {
mPerformList.add("街舞:动动手");
}
@Override
protected void foot() {
mPerformList.add("街舞:动动脚");
}
@Override
protected void turn_around() {
mPerformList.add("街舞:转身");
}
public List<String> getPerformList() {
return mPerformList;
}
}
说明:为了能看出不同的跳舞顺序,我们增加了一个List集合和访问List集合的方法。
- 定义抽象Builder类,即构建对象类:
public abstract class Builder {
public abstract void hand();
public abstract void foot();
public abstract void turn_around();
public abstract Dance create();
}
- 定义构建对象的具体实例类DanceBuilder:
public class DanceBuilder extends Builder {
Dance mDance = new Hiphop();
@Override
public void hand() {
mDance.hand();
}
@Override
public void foot() {
mDance.foot();
}
@Override
public void turn_around() {
mDance.turn_around();
}
@Override
public Dance create() {
return mDance;
}
}
- 定义导演类,负责组装过程:
public class Director {
Builder mBuilder = null;
public Director(Builder builder) {
mBuilder = builder;
}
// 可以定义不同的顺序
public void perform(){
mBuilder.hand();
mBuilder.foot();
mBuilder.turn_around();
}
}
- 进行测试:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Builder builder = new DanceBuilder();
Director director = new Director(builder);
director.perform();
Log.i("fukq","跳舞的顺序是:"+((Hiphop)(builder.create())).getPerformList());
}
}
12-05 16:18:52.241 30387-30387/com.smartisan.builder I/fukq: 跳舞的顺序是:[街舞:动动手, 街舞:动动脚, 街舞:转身]
说明:
① 通过DanceBuilder来构建Dance对象,而Director封装了构建复杂产品对象的过程,而DanceBuilder中有跳舞的不同的顺序,展示了场景一:相同的方法,不同的顺序,带来不同的结果。
② 开发中,Director会被省略,直接使用Builder来进行对象的组装,我们去掉Director类,修改下DanceBuilder类:
public abstract class Builder {
public abstract Dance setHand();
public abstract Dance setFoot();
public abstract Dance setTurn_around();
}
public class DanceBuilder extends Builder {
Dance mDance = new Hiphop();
@Override
public Dance setHand() {
mDance.hand();
return mDance;
}
@Override
public Dance setFoot() {
mDance.foot();
return mDance;
}
@Override
public Dance setTurn_around() {
mDance.turn_around();
return mDance;
}
}
说明:建造者模式通常用Builder进行链式调用(为了呈现链式调用这里修改下方法名),它的关键点在于每个setter方法都返回自身,代码我们可以这样写:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Dance dance = new DanceBuilder().setTurn_around().setFoot().setHand().create();
Log.i("fukq","跳舞的顺序是:"+ ((Hiphop)dance).getPerformList());
}
}
说明:通过链式调用,随意对顺序进行编辑,得到复杂对象Dance。
5. 场景二实现示例:
以AlertDialog为例:
new AlertDialog.Builder(this)
.setIcon(R.mipmap.ic_launcher)
.setTitle("Title")
.setMessage("Messages")
.setPositiveButton("Button01",new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
}).setNegativeButton("Button02",new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
}).setNeutralButton("Button03", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
}).create().show();
说明:通过类名Builder以及链式调用我们可以看出来它是一个建造者模式,通过Builder来组装Dialog的各个部分,我们分析下源码,再次验证下:
public class AlertDialog extends AppCompatDialog implements DialogInterface {
final AlertController mAlert; // 1
...
protected AlertDialog(@NonNull Context context) {
this(context, 0);
}
protected AlertDialog(@NonNull Context context, @StyleRes int themeResId) { // 2
super(context, resolveDialogTheme(context, themeResId));
this.mAlert = new AlertController(this.getContext(), this, this.getWindow());
}
protected AlertDialog(@NonNull Context context, boolean cancelable, @Nullable OnCancelListener cancelListener) {
this(context, 0);
this.setCancelable(cancelable);
this.setOnCancelListener(cancelListener);
}
...
public void setTitle(CharSequence title) {
super.setTitle(title);
this.mAlert.setTitle(title);
}
public void setCustomTitle(View customTitleView) {
this.mAlert.setCustomTitle(customTitleView);
}
public void setMessage(CharSequence message) {
this.mAlert.setMessage(message);
}
...
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.mAlert.installContent();
}
...
public static class Builder { // 3
private final AlertParams P;
private final int mTheme;
public Builder(@NonNull Context context) {
this(context, AlertDialog.resolveDialogTheme(context, 0));
}
public Builder(@NonNull Context context, @StyleRes int themeResId) {
this.P = new AlertParams(new ContextThemeWrapper(context, AlertDialog.resolveDialogTheme(context, themeResId)));
this.mTheme = themeResId;
}
@NonNull
public Context getContext() {
return this.P.mContext;
}
public AlertDialog.Builder setTitle(@StringRes int titleId) {
this.P.mTitle = this.P.mContext.getText(titleId);
return this;
}
public AlertDialog.Builder setTitle(@Nullable CharSequence title) {
this.P.mTitle = title;
return this;
}
public AlertDialog.Builder setCustomTitle(@Nullable View customTitleView) {
this.P.mCustomTitleView = customTitleView;
return this;
}
public AlertDialog.Builder setMessage(@StringRes int messageId) {
this.P.mMessage = this.P.mContext.getText(messageId);
return this;
}
public AlertDialog.Builder setMessage(@Nullable CharSequence message) {
this.P.mMessage = message;
return this;
}
...
public AlertDialog.Builder setView(View view) {
this.P.mView = view;
this.P.mViewLayoutResId = 0;
this.P.mViewSpacingSpecified = false;
return this;
}
...
public AlertDialog create() { // 4
AlertDialog dialog = new AlertDialog(this.P.mContext, this.mTheme);
this.P.apply(dialog.mAlert);
dialog.setCancelable(this.P.mCancelable);
if (this.P.mCancelable) {
dialog.setCanceledOnTouchOutside(true);
}
dialog.setOnCancelListener(this.P.mOnCancelListener);
dialog.setOnDismissListener(this.P.mOnDismissListener);
if (this.P.mOnKeyListener != null) {
dialog.setOnKeyListener(this.P.mOnKeyListener);
}
return dialog;
}
public AlertDialog show() {
AlertDialog dialog = this.create();
dialog.show();
return dialog;
}
}
}
说明:
① 省略号的地方省略一些代码。
② 整个的AlertDialog大分为构造方法、set方法和内部类Builder。
③ 分析源码的顺序是:从调用开始的地方,查看涉及到的所有代码的源码,再来看下调用的代码:
new AlertDialog.Builder(this)
.setTitle("Title")
...
.create().show();
所以这里查看源码的顺序是:先看AlertDialog的内部类Builder,然后再看setTitle方法,再看create方法,最后看show方法。
④ .Builder(this) 的源码:
public Builder(@NonNull Context context) {
this(context, AlertDialog.resolveDialogTheme(context, 0));
}
说明:这里只是传入了上下文this。
⑤ .setTitle("Title")的源码:
private final AlertParams P;
public AlertDialog.Builder setTitle(@Nullable CharSequence title) {
this.P.mTitle = title;
return this;
}
说明:给Title赋值,职责赋值给了AlertParams P中的title。
⑥ .create()的源码:
public AlertDialog create() {
AlertDialog dialog = new AlertDialog(this.P.mContext, this.mTheme);
this.P.apply(dialog.mAlert);
dialog.setCancelable(this.P.mCancelable);
if (this.P.mCancelable) {
dialog.setCanceledOnTouchOutside(true);
}
dialog.setOnCancelListener(this.P.mOnCancelListener);
dialog.setOnDismissListener(this.P.mOnDismissListener);
if (this.P.mOnKeyListener != null) {
dialog.setOnKeyListener(this.P.mOnKeyListener);
}
return dialog;
}
说明:这些代码里面,最可能和title相关的只剩下 this.P.apply(dialog.mAlert); 这一句,所以我们看下这个apply方法的源码:
public void apply(AlertController dialog) {
if (this.mCustomTitleView != null) {
dialog.setCustomTitle(this.mCustomTitleView);
} else {
if (this.mTitle != null) {
dialog.setTitle(this.mTitle);
}
...
}
...
}
说明:apply这个方法是在AlertParams类中,dialog.mAlert 指的就是AlertController,所以apply的作用就是把AlertParams P中的字段值都赋值给AlertController的字段值。
⑦ show()方法从源码来看都是关于如何把Dialog显示在屏幕上的内容,不再附代码,不再分析。
⑧ 通过以上分析我们可以看到,当需要多个参数的时候,可以通过builder模式来传递需要的参数,而且兼容多种参数配置,就像kotlin中的默认参数所带来的便利一样。
6. 后续
如果大家喜欢这篇文章,欢迎点赞!
如果想看更多 设计模式 方面的技术,欢迎关注!