解決的問題:
我創建的這個對象比較複雜,且該對象裏面的成員函數用不同的實現來表示不同的實例,換句話說就是同樣的對象構建過程可以有不同的表示。比如我那天去吃過橋米線,他們有不同的套餐,套餐裏包含的種類是一樣的,都有一碗米線,一份涼菜,一杯飲料。但是不同的套餐裏這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;
//product
class 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;
}
}
};
//builder
class Builder
{
public:
virtual void BuildRiceNoodles() {};
virtual void BuildCoolDish(){};
virtual void BuildDrink(){};
virtual Food * getFood(){return NULL;}
};
//builderA
class 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;
}
};
//builderB
class 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;
}
};
//director
class FoodManager
{
public:
void Construct(Builder * builder)
{
builder->BuildRiceNoodles();
builder->BuildDrink();
builder->BuildCoolDish();
}
};
//clent
int _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;
}
建造者模式的擴展:
建造者模式在使用過程中可以演化出多種形式:
省略抽象建造者角色
如果系統中只需要一個具體的建造者的話,可以省略掉抽象建造者。這是代碼可能如下:
//director
class FoodManager
{
private:
BuilderA * builder;
public:
FoodManager() {builder = new BuilderA();};
void Construct()
{
builder->BuildRiceNoodles();
builder->BuildDrink();
builder->BuildCoolDish();
}
};
省略指導者角色
在具體建造者只有一個的情況下,如果抽象建造者角色已經被省略掉,那麼還可以省略掉指導者角色,讓Builder自己扮演指導者和建造者雙重角色。這是代碼可能如下:
//builder
class Builder
{
private:
Food * food;
public:
Builder(){food = new Food();}
void BuildRiceNoodles() {//..};
void BuildCoolDish(){//..};
void BuildDrink(){//..};
Food * getFood(){return food;}
void Construct()
{
BuildRiceNoodles();
BuildCoolDish();
BuildDrink();
}
};
同時,客戶端也需要進行相應的調整,如下:
//client
int _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】