解決的問題:
我建立的這個對象比較複雜,且該對象裡面的成員函數用不同的實現來表示不同的執行個體,換句話說就是同樣的對象構建過程可以有不同的表示。比如我那天去吃過橋米線,他們有不同的套餐,套餐裡包含的種類是一樣的,都有一碗米線,一份涼菜,一杯飲料。但是不同的套餐裡這3樣又都不是全部一樣的。此時我們就可以用建造者模式。
類圖結構:
1.建造者(Builder)角色:給出一個抽象介面,以規範產品對象的各個組成成分的建造。一般而言,此介面獨立於應用程式的商業邏輯。模式中直接建立產品對象的是具體建造者(Concrete Builder)角色。具體建造者類必須實現這個介面所要求的方法:一個是建造方法,另一個是結果返還方法。此時就是米線店的員工,按照收銀員的要求的去準備具體的套餐,放入適當的米線,涼菜和飲料。
2.具體建造者(Concrete Builder)角色:擔任這個角色的是於應用程式緊密相關的類,它們在應用程式調用下建立產品執行個體。這個角色主要完成的任務包括:實現Builder角色提供的介面,一步一步完成建立產品執行個體的過程。在建造過程完成後,提供產品的執行個體。是具體的做某個套餐的員工。
3.指導者(Director)角色:擔任這個角色的類調用具體建造者角色以建立產品對象。導演者並沒有產品類的具體知識,真正擁有產品類的具體知識的是具體建造者對象。是收銀員,他知道我想要什麼套餐,他會告訴裡面的米線店員工去準備什麼套餐。
4.產品(Product)角色:產品便是建造中的複雜物件。指導者角色是於用戶端打交道的角色。導演者角色將用戶端建立產品的請求劃分為對各個零件的建造請求,再將這些請求委派給具體建造者角色。具體建造者角色是做具體建造工作的,但卻不為用戶端所知。就是最後的套餐,所有東西放到一起端過來。
範例實現:
// CplusplusBuild.cpp : Defines the entry point for the console application.//#include "stdafx.h"#include <vector>#include <string>#include <iostream>using namespace std;//productclass Food{private:vector<string> mFoodName;vector<int> mFoodPrice;public:void add(string foodName,int price){mFoodName.push_back(foodName);mFoodPrice.push_back(price);}void show(){cout<<"Food List" <<endl;cout<<"-------------------"<<endl;for(int i=0;i<mFoodName.size();i++){cout<<mFoodName[i]<<" "<<mFoodPrice[i]<<endl;}}};//builderclass Builder{public:virtual void BuildRiceNoodles() {};virtual void BuildCoolDish(){};virtual void BuildDrink(){};virtual Food * getFood(){return NULL;}};//builderAclass BuilderA:public Builder{private:Food *food;public:BuilderA(){food = new Food();}void BuildRiceNoodles(){food->add("RiceNoodlesA",20);}void BuildCoolDish(){food->add("CoolDishA",20);}void BuildDrink(){food->add("DrinkA",20);}Food * getFood(){return food;}};//builderBclass BuilderB:public Builder{private:Food *food;public:BuilderB(){food = new Food();}void BuildRiceNoodles(){food->add("RiceNoodlesB",10);}void BuildCoolDish(){food->add("CoolDishB",10);}void BuildDrink(){food->add("DrinkB",10);}Food * getFood(){return food;}};//directorclass FoodManager{public:void Construct(Builder * builder){builder->BuildRiceNoodles();builder->BuildDrink();builder->BuildCoolDish();}};//clentint _tmain(int argc, _TCHAR* argv[]){FoodManager *foodManager= new FoodManager();Builder * builder = new Builder();// the following code can use simple factory;char ch;cout<<"input your food Type (A or B):";cin>>ch;if(ch=='A'){builder = new BuilderA();}else if(ch=='B'){builder = new BuilderB();}foodManager->Construct(builder);Food * food = builder->getFood();food->show();return 0;}
建造者模式的擴充:
建造者模式在使用過程中可以演化出多種形式:
省略抽象建造者角色
如果系統中只需要一個具體的建造者的話,可以省略掉抽象建造者。這是代碼可能如下:
//directorclass FoodManager{private:BuilderA * builder;public:FoodManager() {builder = new BuilderA();};void Construct(){builder->BuildRiceNoodles();builder->BuildDrink();builder->BuildCoolDish();}};
省略指導者角色
在具體建造者只有一個的情況下,如果抽象建造者角色已經被省略掉,那麼還可以省略掉指導者角色,讓Builder自己扮演指導者和建造者雙重角色。這是代碼可能如下:
//builderclass Builder{private:Food * food;public: Builder(){food = new Food();} void BuildRiceNoodles() {//..}; void BuildCoolDish(){//..}; void BuildDrink(){//..}; Food * getFood(){return food;} void Construct() { BuildRiceNoodles(); BuildCoolDish(); BuildDrink(); }};
同時,用戶端也需要進行相應的調整,如下:
//clientint _tmain(int argc, _TCHAR* argv[]){Builder * builder = new Builder(); builder->Construct();Food *food = builder->getFood();food->show();return 0;}
C#中的StringBuilder就是這樣一個例子。
實現要點:
1.建造者模式主要用於“分步驟構建一個複雜的對象”,在這其中“每個步驟”是一個穩定的演算法,而複雜物件的各個步驟之間則經常變化。
2.上一篇所說的抽象原廠模式解決“系列對象”的需求變化,而建造者模式解決單個對象裡“對象部分”的需求變化。
產品不需要抽象類別,特別是由於建立對象的演算法複雜而導致使用此模式的情況下或者此模式應用於產品的產生過程,其最終結果可能差異很大,不大可能提煉出一個抽象產品類。
3.建立者中的建立子組件的介面方法不是抽象方法而是空方法,不進行任何操作,具體的建立者只需要覆蓋需要的方法就可以,但是這也不是絕對的,特別是類似文本轉換這種情況下,預設的方法將輸入原封不動的輸出是合理的預設操作。
適用性:
以下情況應當使用建造者模式:
1、需要產生的產品對象有複雜的內部結構。
2、需要產生的產品對象的屬性相互依賴,建造者模式可以強迫產生順序。
3、 在對象建立過程中會使用到系統中的一些其它對象,這些對象在產品對象的建立過程中不易得到。
效果
1、建造者模式的使用使得產品的內部表象可以獨立的變化。使用建造者模式可以使用戶端不必知道產品內部組成的細節。
2、每一個Builder都相對獨立,而與其它的Builder無關。
3、可使對構造過程更加精細控制。
4、將構建代碼和實現代碼分開。
5、建造者模式的缺點在於難於應付“分步驟構建演算法”的需求變動。
LCL_data原創於CSDN.NET【http://blog.csdn.net/lcl_data/article/details/8758477】