1. 單例模式
確保某一個(gè)對象只有一個(gè)實(shí)例,而且自行實(shí)例化并向整個(gè)程序提供這個(gè)實(shí)例。
優(yōu)點(diǎn)
- 由于單例模式在內(nèi)存中只有一個(gè)實(shí)例,減少了內(nèi)存開支,特別是一個(gè)對象需要頻繁地創(chuàng)建、銷毀時(shí),而且創(chuàng)建或銷毀時(shí)性能又無法優(yōu)化,單例模式的優(yōu)勢就非常明顯。
- 減少了系統(tǒng)的性能開銷,當(dāng)一個(gè)對象的產(chǎn)生需要比較多的資源時(shí),如讀取配置、產(chǎn)生其他依賴對象時(shí),則可以通過在應(yīng)用啟動(dòng)時(shí)直接產(chǎn)生一個(gè)單例對象,然后永久駐留內(nèi)存的方式來解決。
- 避免對資源的多重占用。如避免對同一個(gè)資源文件的同時(shí)寫操作。
- 單例模式可以在系統(tǒng)設(shè)置全局的訪問點(diǎn),優(yōu)化和共享資源訪問。
缺點(diǎn)
- 單例模式一般沒有接口,擴(kuò)展困難。
- 不利于測試。
使用場景
- 要求生成唯一序列號的環(huán)境。
- 在整個(gè)項(xiàng)目中需要一個(gè)共享訪問點(diǎn)或共享數(shù)據(jù)。
- 創(chuàng)建一個(gè)對象需要消耗的資源過多,如要訪問IO和數(shù)據(jù)庫等資源。
- 需要定義大量的靜態(tài)常量和靜態(tài)方法的環(huán)境。
實(shí)現(xiàn)
懶漢實(shí)現(xiàn)方法,即實(shí)例化在對象首次被訪問時(shí)進(jìn)行??梢允褂妙惖乃接徐o態(tài)指針變量指向類的唯一實(shí)例,并用一個(gè)公有的靜態(tài)方法獲取該實(shí)例。同時(shí)需將默認(rèn)構(gòu)造函數(shù)聲明為private,防止用戶調(diào)用默認(rèn)構(gòu)造函數(shù)創(chuàng)建對象。
class CSingleton
{
private:
CSingleton(){};
static CSingleton* m_pInstance;
public:
static CSingleton* GetInstance(){
if (m_Instance == NULL )
{
Lock();
if (m_Instance == NULL )
{
m_Instance = new Singleton ();
}
UnLock();
}
return m_pInstance;
}
};
此處進(jìn)行了兩次m_Instance == NULL的判斷,是借鑒了Java的單例模式實(shí)現(xiàn)時(shí),使用的所謂的“雙檢鎖”機(jī)制。因?yàn)檫M(jìn)行一次加鎖和解鎖是需要付出對應(yīng)的代價(jià)的,而進(jìn)行兩次判斷,就可以避免多次加鎖與解鎖操作,同時(shí)也保證了線程安全。
上面的實(shí)現(xiàn)存在一個(gè)問題,就是沒有提供刪除對象的方法。一個(gè)妥善的方法是讓這個(gè)類自己知道在合適的時(shí)候把自己刪除,或者說把刪除自己的操作掛在操作系統(tǒng)中的某個(gè)合適的點(diǎn)上,使其在恰當(dāng)?shù)臅r(shí)候被自動(dòng)執(zhí)行。
我們知道,程序在結(jié)束的時(shí)候,系統(tǒng)會(huì)自動(dòng)析構(gòu)所有的全局變量。事實(shí)上,系統(tǒng)也會(huì)析構(gòu)所有的類的靜態(tài)成員變量,就像這些靜態(tài)成員也是全局變量一樣。利用這個(gè)特征,我們可以在單例類中定義一個(gè)這樣的靜態(tài)成員變量,而它的唯一工作就是在析構(gòu)函數(shù)中刪除單例類的實(shí)例。如下面的代碼中的CGarbo類(Garbo意為垃圾工人):
class CSingleton
{
private:
CSingleton(){};
static CSingleton* m_pInstance;
class CGarbo
{
public:
~CGarbo(){
if(CSingleton::m_pInstance != NULL)
delete CSingleton::m_pInstance;
}
};
static CGarbo garbo;
public:
static CSingleton* GetInstance(){
if (m_Instance == NULL )
{
Lock();
if (m_Instance == NULL )
{
m_Instance = new Singleton ();
}
UnLock();
}
return m_pInstance;
}
};
類CGarbo被定義為CSingleton的私有內(nèi)嵌類,以防該類被在其他地方濫用。程序運(yùn)行結(jié)束時(shí),系統(tǒng)會(huì)調(diào)用CSingleton的靜態(tài)成員Garbo的析構(gòu)函數(shù),該析構(gòu)函數(shù)會(huì)刪除單例的唯一實(shí)例。
餓漢實(shí)現(xiàn)方法:在程序開始時(shí)就自行創(chuàng)建實(shí)例。如果說懶漢式是“時(shí)間換空間”,那么餓漢式就是“空間換時(shí)間”,因?yàn)橐婚_始就創(chuàng)建了實(shí)例,所以每次用到的之后直接返回就好了。
class CSingleton
{
private:
CSingleton(){};
static CSingleton* m_pInstance;
class CGarbo
{
public:
~CGarbo(){
if(CSingleton::m_pInstance != NULL)
delete CSingleton::m_pInstance;
}
};
static CGarbo garbo;
public:
static CSingleton* GetInstance(){
return m_pInstance;
}
};
CSingleton *CSingleton::m_pInstance = new CSingleton();
2. 工廠方法模式
介紹
意圖:定義一個(gè)創(chuàng)建對象的接口,讓其子類自己決定實(shí)例化哪一個(gè)產(chǎn)品類,工廠模式使其創(chuàng)建過程延遲到子類進(jìn)行。
主要解決:主要解決接口選擇的問題。
何時(shí)使用:我們明確地計(jì)劃不同條件下創(chuàng)建不同實(shí)例時(shí)。
如何解決:讓其子類實(shí)現(xiàn)工廠接口,返回的也是一個(gè)抽象的產(chǎn)品。
應(yīng)用實(shí)例:
- 您需要一輛汽車,可以直接從工廠里面提貨,而不用去管這輛汽車是怎么做出來的,以及這個(gè)汽車?yán)锩娴木唧w實(shí)現(xiàn)。
- Hibernate 換數(shù)據(jù)庫只需換方言和驅(qū)動(dòng)就可以。
優(yōu)點(diǎn):
- 一個(gè)調(diào)用者想創(chuàng)建一個(gè)對象,只要知道其名稱就可以了。
- 擴(kuò)展性高,如果想增加一個(gè)產(chǎn)品,只要擴(kuò)展一個(gè)工廠類就可以。
- 屏蔽產(chǎn)品的具體實(shí)現(xiàn),調(diào)用者只關(guān)心產(chǎn)品的接口。
缺點(diǎn):
每次增加一個(gè)產(chǎn)品時(shí),都需要增加一個(gè)具體類和對象實(shí)現(xiàn)工廠,使得系統(tǒng)中類的個(gè)數(shù)成倍增加,在一定程度上增加了系統(tǒng)的復(fù)雜度,同時(shí)也增加了系統(tǒng)具體類的依賴。這并不是什么好事。
**使用場景: **
- 日志記錄器:記錄可能記錄到本地硬盤、系統(tǒng)事件、遠(yuǎn)程服務(wù)器等,用戶可以選擇記錄日志到什么地方。
- 數(shù)據(jù)庫訪問,當(dāng)用戶不知道最后系統(tǒng)采用哪一類數(shù)據(jù)庫,以及數(shù)據(jù)庫可能有變化時(shí)。
- 設(shè)計(jì)一個(gè)連接服務(wù)器的框架,需要三個(gè)協(xié)議,"POP3"、"IMAP"、"HTTP",可以把這三個(gè)作為產(chǎn)品類,共同實(shí)現(xiàn)一個(gè)接口。
注意事項(xiàng):
作為一種創(chuàng)建類模式,在任何需要生成復(fù)雜對象的地方,都可以使用工廠方法模式。有一點(diǎn)需要注意的地方就是復(fù)雜對象適合使用工廠模式,而簡單對象,特別是只需要通過 new 就可以完成創(chuàng)建的對象,無需使用工廠模式。如果使用工廠模式,就需要引入一個(gè)工廠類,會(huì)增加系統(tǒng)的復(fù)雜度。
工廠方法模式:定義一個(gè)用于創(chuàng)建對象的接口,讓子類決定實(shí)例化哪一個(gè)類。工廠方法使一個(gè)類的實(shí)例化延遲到其子類。
實(shí)現(xiàn)
簡單工廠模式
#include <iostream>
using namespace std;
class Shape
{
public:
virtual void draw()=0;
};
class Square : public Shape
{
public:
void draw(){
cout<<"This is a square."<<endl;
}
};
class Rectangle : public Shape
{
public:
void draw(){
cout<<"This is a rectangle."<<endl;
}
};
class Circle :public Shape
{
public:
void draw(){
cout<<"This is a circle"<<endl;
}
};
class SimpleFactory
{
public:
typedef enum ShapeType
{
ShapeSquare,
ShapeRectangle,
ShapeCircle
}SHAPETYPE;
Shape* CreateShape(SHAPETYPE type)
{
switch(type)
{
case ShapeSquare:
return new Square();
case ShapeRectangle:
return new Rectangle();
case ShapeCircle:
return new Circle();
default:
return NULL;
}
}
};
int _tmain(int argc, _TCHAR* argv[])
{
SimpleFactory* myFactory = new SimpleFactory();
Shape* myShape = myFactory->CreateShape(SimpleFactory::ShapeCircle);
myShape->draw();
delete myShape;
myShape = NULL;
delete myFactory;
}
上述實(shí)現(xiàn)需要手動(dòng)釋放申請?jiān)诙焉系膬?nèi)存,可以利用智能指針實(shí)現(xiàn)自動(dòng)釋放。
工廠方法模式
擁有幾個(gè)工廠,都派生自同一抽象工廠類,可以生產(chǎn)不同的產(chǎn)品。
C++設(shè)計(jì)模式——工廠方法模式

class AbstractProduct
{
public:
virtual void operation()=0;
};
class ProductA : public AbstractProduct
{
public:
virtual void operation(){
cout<<"This is A!"<<endl;
}
};
class ProductB : public AbstractProduct
{
public:
virtual void operation(){
cout<<"This is B!"<<endl;
}
};
class AbstractFactory
{
public:
virtual AbstractProduct* createProduct()=0;
};
class FactoryA : public AbstractFactory
{
public:
AbstractProduct* createProduct(){
return new ProductA();
}
};
class FactoryB : public AbstractFactory
{
public:
AbstractProduct* createProduct(){
return new ProductB();
}
};
int main()
{
AbstractFactory* myFactory = new FactoryA();
AbstractProduct* myProduct = myFactory->createProduct();
myProduct->operation();
delete myProduct;
myProduct = NULL;
delete myFactory;
myFactory = NULL;
}
注意:在上述兩個(gè)實(shí)現(xiàn)中,為簡單起見,并沒有定義構(gòu)造函數(shù)和虛構(gòu)函數(shù)。其中,基類的虛構(gòu)函數(shù)應(yīng)為虛函數(shù),這樣才能保證,delete操作能夠調(diào)用合適的析構(gòu)函數(shù)。
抽象工廠模式
擁有幾個(gè)抽象工廠類。
3. 建造者模式
將一個(gè)復(fù)雜對象的構(gòu)建與它的表示分離,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示。
介紹
主要解決在軟件系統(tǒng)中,有時(shí)候面臨著"一個(gè)復(fù)雜對象"的創(chuàng)建工作,其通常由各個(gè)部分的子對象用一定的算法構(gòu)成;由于需求的變化,這個(gè)復(fù)雜對象的各個(gè)部分經(jīng)常面臨著劇烈的變化,但是將它們組合在一起的算法卻相對穩(wěn)定。
何時(shí)使用:一些基本部件不會(huì)變,而其組合經(jīng)常變化的時(shí)候。
如何解決:將變與不變分離開。
應(yīng)用實(shí)例:
- 去肯德基,漢堡、可樂、薯?xiàng)l、炸雞翅等是不變的,而其組合是經(jīng)常變化的,生成出所謂的"套餐"。
- JAVA 中的 StringBuilder。
**優(yōu)點(diǎn): **
- 建造者獨(dú)立,易擴(kuò)展。
- 便于控制細(xì)節(jié)風(fēng)險(xiǎn)。
**缺點(diǎn): **
- 產(chǎn)品必須有共同點(diǎn),范圍有限制。
- 如內(nèi)部變化復(fù)雜,會(huì)有很多的建造類。
**使用場景: **
- 需要生成的對象具有復(fù)雜的內(nèi)部結(jié)構(gòu)。
- 需要生成的對象內(nèi)部屬性本身相互依賴。
注意事項(xiàng):與工廠模式的區(qū)別是:建造者模式更加關(guān)注與零件裝配的順序。