0. Introduction
接觸設計模式有兩年時間了,但一直沒有系統整理過,為了不至于讓自己的思維被繁瑣的工作一點點禁錮,還是決定總結一下,為了能夠真正做到有所收獲,整個系列會按照GoF的Design Patterns: Elements of Reusable Object-Oriented Software的行文思路,但不會照本宣科就是了,Wikipedia上關于23種設計模式的介紹非常全面,CSDN上也可以下載中/英文電子檔,因此很多套話、類圖一概省去。
最早接觸設計模式的時候,難免被各種模式的聯系和區別所困擾,從教科書的分析可以得到模式之間形式上的不同。但這樣對于領會設計模式意義不大,因為我們掌握模式的目的是為了融會貫通,靈活運用,以對開發有所幫助。
稍微成規模的OO程序,會有大量對象,其中很多實體對象之間存在著父子、兄弟關系,對象的創建提升為一種模式。其好處在于設計模式本身所宣稱的reusable,這就像堆積木蓋房子一樣,堆的好的情況下,換一換門窗便是另一番風景。
關于實現,我不會為了厘清模式間的區別而刻意使用相似代碼實現,相反,我會根據模式本身的適用情況舉例,而且大量代碼基于SourceMaking。
_______________________________
1. Creational Design Patterns(DP)
創建型DP抽象了類和對象的創建過程,GoF給出了5種創建型DP:Abstract Factory、Builder、Factory Method、Builder、Prototype、Singleton。
2. Abstract Factory
意圖:提供一個創建一系列相關或相互依賴對象的接口,而無需指定它們具體的類。
1) 只提供了一個創建接口,其返回值為具體產品:如AbstractProduct *Client::CreateProduct(AbstractFactory &factory);
2) 接口的參數是一個工廠對象(AbstractFactory &factory
)的引用,參數類型(AbstractFactory
)為抽象基類,調用時根據需要傳入具體工廠對象即可;
3) 接口內部實現了一系列相關或相互依賴對象(抽象產品)的創建:當傳入具體工廠時,接口實現的就是一系列具體產品的創建;
4) 創建的產品立即返回(CreateProduct
)。
參與者:
• AbstractFactory
— 聲明一個創建抽象產品對象的操作接口。
• ConcreteFactory
— 實現創建具體產品對象的操作。
• AbstractProduct
— 為一類產品對象聲明一個接口。
• ConcreteProduct
— 定義一個將被相應的具體工廠創建的產品對象。
— 實現AbstractProduct接口。
• Client
— 僅使用由AbstractFactory和AbstractProduct類聲明的接口。
代碼:
class AbstractFactory
{
public:
virtual AbstractProduct *MakePartA() = 0;
virtual AbstractProduct *MakePartB() = 0;
virtual AbstractProduct *MakePartC() = 0;
virtual AbstractProduct *AddPart(const AbstractProduct *pPart) = 0;
};
AbstractProduct *Client::CreateProduct(AbstractFactory &factory)
{
AbstractProduct *pProduct = factory.CreateProduct();
AbstractProduct *pPartA = factory.MakePartA();
AbstractProduct *pPartB = factory.MakePartB();
AbstractProduct *pPartC = factory.MakePartC();
factory.AddPart(pPartA);
factory.AddPart(pPartB);
factory.AddPart(pPartC);
return pProduct;
}
int main(void)
{
Client client;
ConcreteFactory factory;
client.CreateProduct(factory);
return 0;
}
3. Builder
意圖:將一個復雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。
1) director提供抽象產品創建接口:如void Director::Construct();
2) 不同產品使用同一創建過程,由director指定特定builder以生產不同產品;
3) 接口內部實現了一個復雜對象(抽象產品)的創建:當傳入具體工廠時,接口實現的是一個復雜的具體產品的創建;
4) 創建的產品并不立即返回,創建完畢后返回,或使用接口(GetProduct
)提取結果。
參與者:
• Builder
— 為創建一個Product對象的各個部件指定抽象接口。
• ConcreteBuilder
— 實現Builder的接口以構造和裝配該產品的各個部件。
— 定義并明確它所創建的表示。
— 提供一個檢索產品的接口。
• Director
— 構造一個使用Builder接口的對象。
• Product
— 表示被構造的復雜對象。ConcreteBuilder創建該產品的內部表示并定義它的裝配過程。
— 包含定義組成部件的類,包括將這些部件裝配成最終產品的接口。
代碼:
class Builder
{
public:
virtual void MakePartA() = 0;
virtual void MakePartB() = 0;
virtual void MakePartC() = 0;
Product *GetProduct() { return _product; }
protected:
Product *_product;
};
class Director
{
public:
void setBuilder(Builder *b) { _builder = b; }
void Construct();
private:
Builder *_builder;
};
void Director::Construct()
{
_builder.MakePartA();
_builder.MakePartB();
_builder.MakePartC();
}
int main(void) {
ConcreteBuilderA concreteBuilderA;
ConcreteBuilderB concreteBuilderB;
Director director;
Product *pProduct;
director.SetBuilder(&concreteBuilderA);
director.Construct();
pProduct = concreteBuilderA.GetProduct();
pProduct->Show();
director.SetBuilder(&concreteBuilderB);
director.Construct();
pProduct = concreteBuilderB.GetProduct();
pProduct->Show();
return 0;
}
4. Factory Method
意圖:定義一個用于創建對象的接口,讓子類決定實例化哪一個類。Factory Method使一個類的實例化延遲到其子類。
1) 看得出該模式其實就是C++的多態特性,借繼承實現。因此,其別名為虛構造器( Virtual Constructor);
2) 作為模式與C++多態特性不同的是,Creator可以定義工廠方法的缺省實現,完成缺省操作,MFC大量使用了這一思想。
參與者:
• Product
— 定義工廠方法所創建的對象的接口。
• ConcreteProduct
— 實現Product接口。
• Creator
— 聲明工廠方法,該方法返回一個Product類型的對象。Creator也可以定義一個工廠方法的缺省實現,它返回一個缺省的ConcreteProduct對象。
— 可以調用工廠方法以創建一個Product對象。
• ConcreteCreator
— 重定義工廠方法以返回一個ConcreteProduct實例。
代碼:
ConcreteProduct *ConcreteCreator::FactoryMethod()
{
ConcreteProduct
*pProduct = new ConcreteProduct
;
return pProduct;
}
Product *Creator::FactoryMethod()
{
Product *pProduct = new Product;
return pProduct;
}
int main(void) {
Creator creator;
ConcreteProduct *pProduct;
pProduct = creator.FactoryMethod();
pProduct->Show();
return 0;
}
5. Prototype
意圖:用原型實例指定創建對象的種類,并且通過拷貝這些原型創建新的對象。
1) 創建不再通過工廠新類繼承(inheritance),而是通過委托(delegation);
2) 根通拷貝原型實例創建新對象。
參與者:
• ProtoType
— 聲明一個克隆自身的接口。
• ConcreteProtoType
— 實現一個克隆自身的操作。
• Client
— 讓一個原型克隆自身從而創建一個新的對象。
代碼:
class ProtoType
{
public:
virtual void Draw();
virtual ProtoType *Clone() = 0;
virtual void Initialize();
};
class ProtoTypeA: public ProtoType
{
public:
virtual ProtoType *Clone()
{
return new ProtoTypeA;
}
};
class ProtoTypeB: public ProtoType
{
public:
virtual ProtoType *Clone()
{
return new ProtoTypeB;
}
};
class Client
{
public:
static ProtoType *Clone( int choice );
private:
static ProtoType *s_prototypes[3];
};
ProtoType* Client::s_prototypes[] = { 0, new ProtoTypeA, new ProtoTypeB };
ProtoType *Client::Clone(int choice)
{
return s_prototypes[choice]->Clone();
}
6. Singleton
意圖:保證一個類僅有一個實例,并提供一個訪問它的全局訪問點。
1) 用靜態成員函數保證上述意圖。
參與者:
• Singleton
— 定義一個Instance操作,允許客戶訪問它的唯一實例。Instance是一個類操作(即C++中的一個靜態成員函數)。
— 可能負責創建它自己的唯一實例。
代碼:
class Singleton
{
public:
static Singleton *GetInstance()
{
if (!s_instance)
s_instance = new Singleton;
return s_instance;
}
void Run() {}
private:
static Singleton *s_instance;
Singleton() {} // Singleton cannot be created outside.
};
Singleton *GetSingleton(void)
{
return Singleton::GetInstance();
}
int main(void)
{
GetSingleton()->Run();
return 0;
}
______________________________________________
代碼寫的都比較簡單,基本可以將各種模式之間的不同體現出來了。