0. 序言
- 建造者模式的定義:將一個(gè)復(fù)雜對(duì)象的構(gòu)建與它的表示分離殷勘,使得同樣的構(gòu)建過(guò)程可以創(chuàng)建不同的表示舷礼。
1. 介紹
- 建造者模式是一步一步創(chuàng)建一個(gè)復(fù)雜對(duì)象的創(chuàng)建型模式赴邻,允許在不知道內(nèi)部構(gòu)建細(xì)節(jié)的情況下握恳,可以更精細(xì)地控制對(duì)象的構(gòu)造流程迟隅。也就是它注重的是構(gòu)建對(duì)象的構(gòu)成梭纹,而不是構(gòu)件對(duì)象過(guò)程中所需要的部件表示的細(xì)節(jié)躲惰。
- 舉例:賈躍亭會(huì)計(jì)要造一臺(tái)FF,而一臺(tái)FF需要輪胎变抽、方向盤(pán)础拨、發(fā)動(dòng)機(jī)等部件。而賈躍亭關(guān)心的是如何組裝這些零件為一輛車绍载,而不是關(guān)心這個(gè)輪胎怎么造的诡宗,這個(gè)方向盤(pán)怎么造的。
- 優(yōu)點(diǎn)就是構(gòu)建過(guò)程和部件可以自由擴(kuò)展击儡,兩者之間的耦合降到最低僚焦。
2. 場(chǎng)景
- 相同的方法,不同的執(zhí)行順序曙痘,產(chǎn)生不同的結(jié)果芳悲。
舉例:街舞大家都看過(guò),有時(shí)候是動(dòng)手边坤、動(dòng)腳名扛、轉(zhuǎn)身,有時(shí)候是動(dòng)腳茧痒、轉(zhuǎn)身肮韧、動(dòng)手等等,不同的行動(dòng)方式旺订,呈現(xiàn)不同的美弄企。 - 初始化一個(gè)對(duì)象特別復(fù)雜,如參數(shù)多区拳。
舉例:初始化一個(gè)AlertDialog或者一個(gè)notification拘领,有的需要標(biāo)題、內(nèi)容兩項(xiàng)樱调,有的需要標(biāo)題约素、內(nèi)容届良、進(jìn)度條三項(xiàng)等等,這個(gè)時(shí)候如果通過(guò)構(gòu)造方法初始化參數(shù)的話圣猎,會(huì)有很多種搭配組合士葫,就要寫(xiě)很多構(gòu)造方法,這個(gè)時(shí)候我們就可以使用建造者模式送悔,需要哪個(gè)參數(shù)我們就拿每個(gè)參數(shù)對(duì)應(yīng)的方法就行慢显。
3. UML類圖(用PC觀看)
角色介紹:
① Product產(chǎn)品類——產(chǎn)品的抽象類。
② Builder——抽象Builder類欠啤,規(guī)范產(chǎn)品的組件鳍怨。
③ ConcreteBuilder——具體的Builder類,實(shí)現(xiàn)具體的組件過(guò)程跪妥。
④ Director——統(tǒng)一組裝過(guò)程
4. 場(chǎng)景一實(shí)現(xiàn)示例
以跳舞為例:
- 定義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("街舞:動(dòng)動(dòng)手");
}
@Override
protected void foot() {
mPerformList.add("街舞:動(dòng)動(dòng)腳");
}
@Override
protected void turn_around() {
mPerformList.add("街舞:轉(zhuǎn)身");
}
public List<String> getPerformList() {
return mPerformList;
}
}
說(shuō)明:為了能看出不同的跳舞順序眉撵,我們?cè)黾恿艘粋€(gè)List集合和訪問(wèn)List集合的方法。
- 定義抽象Builder類落塑,即構(gòu)建對(duì)象類:
public abstract class Builder {
public abstract void hand();
public abstract void foot();
public abstract void turn_around();
public abstract Dance create();
}
- 定義構(gòu)建對(duì)象的具體實(shí)例類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;
}
}
- 定義導(dǎo)演類纽疟,負(fù)責(zé)組裝過(guò)程:
public class Director {
Builder mBuilder = null;
public Director(Builder builder) {
mBuilder = builder;
}
// 可以定義不同的順序
public void perform(){
mBuilder.hand();
mBuilder.foot();
mBuilder.turn_around();
}
}
- 進(jìn)行測(cè)試:
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: 跳舞的順序是:[街舞:動(dòng)動(dòng)手, 街舞:動(dòng)動(dòng)腳, 街舞:轉(zhuǎn)身]
說(shuō)明:
① 通過(guò)DanceBuilder來(lái)構(gòu)建Dance對(duì)象,而Director封裝了構(gòu)建復(fù)雜產(chǎn)品對(duì)象的過(guò)程憾赁,而DanceBuilder中有跳舞的不同的順序污朽,展示了場(chǎng)景一:相同的方法,不同的順序龙考,帶來(lái)不同的結(jié)果蟆肆。
② 開(kāi)發(fā)中,Director會(huì)被省略晦款,直接使用Builder來(lái)進(jìn)行對(duì)象的組裝炎功,我們?nèi)サ鬌irector類,修改下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;
}
}
說(shuō)明:建造者模式通常用Builder進(jìn)行鏈?zhǔn)秸{(diào)用(為了呈現(xiàn)鏈?zhǔn)秸{(diào)用這里修改下方法名)缓溅,它的關(guān)鍵點(diǎn)在于每個(gè)setter方法都返回自身蛇损,代碼我們可以這樣寫(xiě):
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());
}
}
說(shuō)明:通過(guò)鏈?zhǔn)秸{(diào)用,隨意對(duì)順序進(jìn)行編輯坛怪,得到復(fù)雜對(duì)象Dance淤齐。
5. 場(chǎng)景二實(shí)現(xiàn)示例:
以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();
說(shuō)明:通過(guò)類名Builder以及鏈?zhǔn)秸{(diào)用我們可以看出來(lái)它是一個(gè)建造者模式,通過(guò)Builder來(lái)組裝Dialog的各個(gè)部分袜匿,我們分析下源碼更啄,再次驗(yàn)證下:
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;
}
}
}
說(shuō)明:
① 省略號(hào)的地方省略一些代碼。
② 整個(gè)的AlertDialog大分為構(gòu)造方法居灯、set方法和內(nèi)部類Builder锈死。
③ 分析源碼的順序是:從調(diào)用開(kāi)始的地方贫堰,查看涉及到的所有代碼的源碼,再來(lái)看下調(diào)用的代碼:
new AlertDialog.Builder(this)
.setTitle("Title")
...
.create().show();
所以這里查看源碼的順序是:先看AlertDialog的內(nèi)部類Builder待牵,然后再看setTitle方法其屏,再看create方法,最后看show方法缨该。
④ .Builder(this) 的源碼:
public Builder(@NonNull Context context) {
this(context, AlertDialog.resolveDialogTheme(context, 0));
}
說(shuō)明:這里只是傳入了上下文this偎行。
⑤ .setTitle("Title")的源碼:
private final AlertParams P;
public AlertDialog.Builder setTitle(@Nullable CharSequence title) {
this.P.mTitle = title;
return this;
}
說(shuō)明:給Title賦值,職責(zé)賦值給了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;
}
說(shuō)明:這些代碼里面蛤袒,最可能和title相關(guān)的只剩下 this.P.apply(dialog.mAlert); 這一句,所以我們看下這個(gè)apply方法的源碼:
public void apply(AlertController dialog) {
if (this.mCustomTitleView != null) {
dialog.setCustomTitle(this.mCustomTitleView);
} else {
if (this.mTitle != null) {
dialog.setTitle(this.mTitle);
}
...
}
...
}
說(shuō)明:apply這個(gè)方法是在AlertParams類中膨更,dialog.mAlert 指的就是AlertController妙真,所以apply的作用就是把AlertParams P中的字段值都賦值給AlertController的字段值。
⑦ show()方法從源碼來(lái)看都是關(guān)于如何把Dialog顯示在屏幕上的內(nèi)容荚守,不再附代碼珍德,不再分析。
⑧ 通過(guò)以上分析我們可以看到矗漾,當(dāng)需要多個(gè)參數(shù)的時(shí)候锈候,可以通過(guò)builder模式來(lái)傳遞需要的參數(shù),而且兼容多種參數(shù)配置敞贡,就像kotlin中的默認(rèn)參數(shù)所帶來(lái)的便利一樣泵琳。
6. 后續(xù)
如果大家喜歡這篇文章,歡迎點(diǎn)贊誊役!
如果想看更多 設(shè)計(jì)模式 方面的技術(shù)获列,歡迎關(guān)注!