前言
建造者模式是設(shè)計(jì)模式的一種,將一個(gè)復(fù)雜對(duì)象的構(gòu)建與它的表示分離,使得同樣的構(gòu)建過(guò)程可以創(chuàng)建不同的表示。
1、建造者模式特征
1.1 建造者模式UML圖
Builder設(shè)計(jì)模式UML圖
1.2 建造者模式角色
在這樣的設(shè)計(jì)模式中,有以下幾個(gè)角色:
- builder:為創(chuàng)建一個(gè)產(chǎn)品對(duì)象的各個(gè)部件指定抽象接口。
- ConcreteBuilder:實(shí)現(xiàn)Builder的接口以構(gòu)造和裝配該產(chǎn)品的各個(gè)部件,定義并明確它所創(chuàng)建的表示,并 提供一個(gè)檢索產(chǎn)品的接口。
- Director:構(gòu)造一個(gè)使用Builder接口的對(duì)象。
- Product:表示被構(gòu)造的復(fù)雜對(duì)象。ConcreteBuilder創(chuàng)建該產(chǎn)品的內(nèi)部表示并定義它的裝配過(guò)程,包含定義組成部件的類,包括將這些部件裝配成最終產(chǎn)品的接口。
1.3 建造者模式使用場(chǎng)景
- 創(chuàng)建復(fù)雜對(duì)象的算法獨(dú)立于組成對(duì)象的部件
- 同一個(gè)創(chuàng)建過(guò)程需要有不同的內(nèi)部表象的產(chǎn)品對(duì)象
2、建造者模式代碼實(shí)現(xiàn)
2.1 標(biāo)準(zhǔn)寫法
builder:
/**
* 抽象建造者
* Created by Administrator on 2018/1/24.
*/
public interface Builder {
public void makeWindow();
public void makeFloor();
public Room build();
}
ConcreteBuilder:
/**
* Created by Administrator on 2018/1/24.
* 持有對(duì)房子的引用
*/
public class WorkBuilder implements Builder {
Room room = new Room();
@Override
public void makeWindow() {
room.setWindow("歐式窗戶");
}
@Override
public void makeFloor() {
room.setFloor("日式地板");
}
@Override
public Room build() {
return room;
}
}
Director:
/**
* Created by Administrator on 2018/1/24.
* 設(shè)計(jì)者(指導(dǎo)者)
*
* 他知道 房屋怎么設(shè)計(jì)
* 他肯定對(duì) 工人所具備的能力有所理解
*/
public class Designer {
public Room build(Builder builder){
builder.makeFloor();
builder.makeWindow();
return builder.build();
}
}
Product:
/**
* Created by Administrator on 2018/1/24.
*/
public class Room {
private String window;
private String floor;
public String getWindow() {
return window;
}
public void setWindow(String window) {
this.window = window;
}
public String getFloor() {
return floor;
}
public void setFloor(String floor) {
this.floor = floor;
}
@Override
public String toString() {
return "Room{" +
"window='" + window + '\'' +
", floor='" + floor + '\'' +
'}';
}
}
Client調(diào)用:
public class Client {
public static void main(String[] args) {
Builder build = new WorkBuilder();
Designer designer = new Designer();
Room room = designer.build(build);
System.out.println(room.toString());
//房間暴露了構(gòu)建過(guò)程不合理
room.setFloor("XXXXX");
room.setWindow("XXXXX");
}
}
運(yùn)行結(jié)果:
Room{window='歐式窗戶', floor='日式地板'}
注意:Product本身暴露了產(chǎn)品構(gòu)建過(guò)程不合理
2.2 變種優(yōu)化寫法
Product:
public class Room {
private String window;
private String floor;
private String lamp;
public Room apply(WorkBuilder.RoomParams params){
window = params.window;
floor = params.floor;
lamp = params.floor;
return this;
}
@Override
public String toString() {
return "Room{" +
"window='" + window + '\'' +
", floor='" + floor + '\'' +
", lamp='" + lamp + '\'' +
'}';
}
}
ConcreteBuilder:
含有和Product同樣參數(shù)的內(nèi)部類RoomParams
public class WorkBuilder {
Room room = new Room();
private RoomParams params = new RoomParams();
public WorkBuilder makeWindow(String window){
params.window = window;
return this;
}
public WorkBuilder makeFloor(String floor){
params.floor = floor;
return this;
}
public WorkBuilder makeLamp(String lamp){
params.lamp = lamp;
return this;
}
/**
* 隱藏構(gòu)建過(guò)程
*真正的構(gòu)建者
* */
public Room build(){
room.apply(params);
return room;
}
public class RoomParams{
public String window;
public String floor;
public String lamp;
}
}
Client調(diào)用:
public class Client {
public static void main(String[] args){
//隱藏了構(gòu)建過(guò)程合理
Room room = new WorkBuilder()
.makeFloor("日式地板")
.makeWindow("歐式窗戶")
.makeLamp("中式吊燈")
.build() ;
System.out.println(room.toString());
}
}
運(yùn)行結(jié)果:
Room{window='歐式窗戶', floor='日式地板', lamp='日式地板'}
Product類隱藏構(gòu)建過(guò)程,設(shè)計(jì)合理
3、Android源碼中使用
建造者典型例子是AlertDialog,它的基本寫法是:
public class AlertDialog extends Dialog implements DialogInterface {
private AlertController mAlert;
protected AlertDialog(Context context) {
this(context, resolveDialogTheme(context, 0), true);
}
@Override
public void setTitle(CharSequence title) {
super.setTitle(title);
mAlert.setTitle(title);
}
public void setMessage(CharSequence message) {
mAlert.setMessage(message);
}
public static class Builder {
private final AlertController.AlertParams P;
private int mTheme;
public Builder(Context context, int theme) {
P = new AlertController.AlertParams(new ContextThemeWrapper(
context, resolveDialogTheme(context, theme)));
mTheme = theme;
}
public Builder setTitle(int titleId) {
P.mTitle = P.mContext.getText(titleId);
return this;
}
public Builder setMessage(CharSequence message) {
P.mMessage = message;
return this;
}
public Builder setOnCancelListener(OnCancelListener onCancelListener) {
P.mOnCancelListener = onCancelListener;
return this;
}
public AlertDialog create() {
final AlertDialog dialog = new AlertDialog(P.mContext, mTheme, false);
P.apply(dialog.mAlert);
dialog.setCancelable(P.mCancelable);
if (P.mCancelable) {
dialog.setCanceledOnTouchOutside(true);
}
dialog.setOnCancelListener(P.mOnCancelListener);
...
return dialog;
}
public AlertDialog show() {
AlertDialog dialog = create();
dialog.show();
return dialog;
}
}
}