建造者模式【Builder Pattern 】
在模板方法模式時用到了公司生產馬車的例子。這裏繼續,客戶有了新的要求,就是能夠控制基本方法的執行順序。
先看一下類圖:
定義一個車輛模型的抽象類,所有的車輛模型都繼承這個類
public abstract class CarModel {
//這個參數是各個基本方法執行的順序
private ArrayList<String> sequence = new ArrayList<String>();
/*
* 模型是啓動開始跑了
*/
protected abstract void start();
//能發動,那還要能停下來,那纔是真本事
protected abstract void stop();
//喇叭會出聲音,是滴滴叫,還是嗶嗶叫
protected abstract void alarm();
//引擎會轟隆隆的響,不響那是假的
protected abstract void engineBoom();
//那模型應該會跑吧,別管是人推的,還是電力驅動,總之要會跑
final public void run() {
//循環一遍,誰在前,就先執行誰
for(int i=0;i<this.sequence.size();i++){
String actionName = this.sequence.get(i);
if(actionName.equalsIgnoreCase("start")){ //如果是start關鍵字,
this.start(); //開啓汽車
}else if(actionName.equalsIgnoreCase("stop")){ //如果是stop關鍵字
this.stop(); //停止汽車
}else if(actionName.equalsIgnoreCase("alarm")){ //如果是alarm關鍵字
this.alarm(); //喇叭開始叫了
}else if(actionName.equalsIgnoreCase("engine boom")){ //如果是engine
boom關鍵字
this.engineBoom(); //引擎開始轟鳴
}
}
//把傳遞過來的值傳遞到類內
final public void setSequence(ArrayList<String> sequence){
this.sequence = sequence;
}
}
然後定義要生產的奔馳、寶馬類。
最後在主類設置執行的順序,進行調用:
BenzModel benz = new BenzModel();
ArrayList<String> sequence = new ArrayList<String>(); //存放run的順序
sequence.add("engine boom"); //客戶要求,run的時候時候先發動引擎
sequence.add("start"); //啓動起來
sequence.add("stop"); //開了一段就停下來
//然後我們把這個順序給奔馳車:
benz.setSequence(sequence);
benz.run();
其中 setSequence 方法是允許客戶自己設置一個順序,是要先跑起來在有引擎聲音還是先有引擎聲音再跑起來,還是說那個喇叭就不要響,對於一個具體的模型永遠都固定的。這樣的設計體現不出程序的擴展性。
接下來客戶可能提各種各種的需求,不同種類的車型可以有不同的執行順序,有的可以不執行。於是乎,先看一下下面的類圖設計:
增加了一個 CarBuilder 的抽象類,以及兩個實現類,其目的是你要什麼排列順序的車,我就給你什麼順序的車,那我們先看 CarBuilder的定義:
public abstract class CarBuilder {
//建造一個模型,你要給我一個順序要,就是組裝順序
public abstract void setSequence(ArrayList<String> sequence);
//設置完畢順序後,就可以直接拿到這個車輛模型
public abstract CarModel getCarModel();
}
實現類可以定義如下:
public class BenzBuilder extends CarBuilder {
private BenzModel benz = new BenzModel();
public CarModel getCarModel() {
return this.benz;
}
public void setSequence(ArrayList<String> sequence) {
this.benz.setSequence(sequence);
}
}
在主類中進行調用:
ArrayList<String> sequence = new ArrayList<String>(); //存放run的順序
sequence.add("engine boom"); //客戶要求,run的時候時候先發動引擎
sequence.add("start"); //啓動起來
sequence.add("stop"); //開了一段就停下來
//要一個奔馳車:
BenzBuilder benzBuilder = new BenzBuilder();
//把順序給這個builder類,製造出這樣一個車出來
benzBuilder.setSequence(sequence);
//製造出一個奔馳車
BenzModel benz = (BenzModel)benzBuilder.getCarModel();
//奔馳車跑一下看看
benz.run();
這樣便可以針對不同的車子的不同需求進行不同的設計建造。
但是如果要有不同的組合呢?類似於有一個導演指揮這演員的演出順序等。那我們設計一個導演類來擴展一下。先看類圖:
增加了Director類:代碼實現
public class Director {
private ArrayList<String> sequence = new ArrayList();
private BenzBuilder benzBuilder = new BenzBuilder();
private BMWBuilder bmwBuilder = new BMWBuilder();
/*
* A類型的奔馳車模型,先start,然後stop,其他什麼引擎了,喇叭一概沒有
*/
public BenzModel getABenzModel(){
//清理場景,這裏是一些初級程序員不注意的地方
this.sequence.clear();
//這隻ABenzModel的執行順序
this.sequence.add("start");
this.sequence.add("stop");
//按照順序返回一個奔馳車
this.benzBuilder.setSequence(this.sequence);
return (BenzModel)this.benzBuilder.getCarModel();
}
/*
* B型號的奔馳車模型,是先發動引擎,然後啓動,然後停止,沒有喇叭
*/
public BenzModel getBBenzModel(){
this.sequence.clear();
this.sequence.add("engine boom");
this.sequence.add("start");
this.sequence.add("stop");
this.benzBuilder.setSequence(this.sequence);
return (BenzModel)this.benzBuilder.getCarModel();
}
...
}
在這個導演類中就可以任意的指派方法的執行順序了
於是,在Client方法中調用只需如下:
public class Client {
public static void main(String[] args) {
Director director = new Director();
//1W輛A類型的奔馳車
for(int i=0;i<10000;i++){
director.getABenzModel().run();
}
//100W輛B類型的奔馳車
for(int i=0;i<1000000;i++){
director.getBBenzModel().run();
}
//1000W量C類型的寶馬車
for(int i=0;i<10000000;i++){
director.getCBMWModel().run();
}
}
}
對該案例的總結:
(1)CarModel 以及兩個實現類 BenzModel 和 BMWModel 叫做產品類(Product Class),這個產品類實現了模板方法模式,也就是有模板方法和基本方法。
(2)CarBuilder 以及兩個實現類 BenzBuilder 和 BMWBuilder 叫做建造者(Builder Class)。
(3)Director 類叫做導演類(Director Class),負責安排已有模塊的順序,然後告訴 Builder 開始建造
這就已經實現了建造者模式了。
總結:
四個要素
產品類:一般是一個較爲複雜的對象,也就是說創建對象的過程比較複雜,一般會有比較多的代碼量。在本類圖中,產品類是一個具體的類,而非抽象類。實際編程中,產品類可以是由一個抽象類與它的不同實現組成,也可以是由多個抽象類與他們的實現組成。
抽象建造者:引入抽象建造者的目的,是爲了將建造的具體過程交與它的子類來實現。這樣更容易擴展。一般至少會有兩個抽象方法,一個用來建造產品,一個是用來返回產品。
建造者:實現抽象類的所有未實現的方法,具體來說一般是兩項任務:組建產品;返回組建好的產品。
導演類:負責調用適當的建造者來組建產品,導演類一般不與產品類發生依賴關係,與導演類直接交互的是建造者類。一般來說,導演類被用來封裝程序中易變的部分。
注意與工廠設計模式的區別
工廠設計模式側重於生產的過程,即對象的創建,而建造者模式側重的是通過不同的執行順序,讓產品類中的調用順序不同產生了不同的效能。
使用建造者模式的好處:
1.使用建造者模式可以使客戶端不必知道產品內部組成的細節。
2.具體的建造者類之間是相互獨立的,對系統的擴展非常有利。
3.由於具體的建造者是獨立的,因此可以對建造過程逐步細化,而不對其他的模塊產生任何影響。
使用建造模式的場合:
1.創建一些複雜的對象時,這些對象的內部組成構件間的建造順序是穩定的,但是對象的內部組成構件面臨着複雜的變化。
2.要創建的複雜對象的算法,獨立於該對象的組成部分,也獨立於組成部分的裝配方法時。