設(shè)計(jì)模式——單例模式與工廠模式

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();

"C++設(shè)計(jì)模式——單例模式"

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í)例:

  1. 您需要一輛汽車,可以直接從工廠里面提貨,而不用去管這輛汽車是怎么做出來的,以及這個(gè)汽車?yán)锩娴木唧w實(shí)現(xiàn)。
  2. Hibernate 換數(shù)據(jù)庫只需換方言和驅(qū)動(dòng)就可以。

優(yōu)點(diǎn):

  1. 一個(gè)調(diào)用者想創(chuàng)建一個(gè)對象,只要知道其名稱就可以了。
  2. 擴(kuò)展性高,如果想增加一個(gè)產(chǎn)品,只要擴(kuò)展一個(gè)工廠類就可以。
  3. 屏蔽產(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)具體類的依賴。這并不是什么好事。

**使用場景: **

  1. 日志記錄器:記錄可能記錄到本地硬盤、系統(tǒng)事件、遠(yuǎn)程服務(wù)器等,用戶可以選擇記錄日志到什么地方。
  2. 數(shù)據(jù)庫訪問,當(dāng)用戶不知道最后系統(tǒng)采用哪一類數(shù)據(jù)庫,以及數(shù)據(jù)庫可能有變化時(shí)。
  3. 設(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í)例:

  1. 去肯德基,漢堡、可樂、薯?xiàng)l、炸雞翅等是不變的,而其組合是經(jīng)常變化的,生成出所謂的"套餐"。
  2. JAVA 中的 StringBuilder。

**優(yōu)點(diǎn): **

  1. 建造者獨(dú)立,易擴(kuò)展。
  2. 便于控制細(xì)節(jié)風(fēng)險(xiǎn)。

**缺點(diǎn): **

  1. 產(chǎn)品必須有共同點(diǎn),范圍有限制。
  2. 如內(nèi)部變化復(fù)雜,會(huì)有很多的建造類。

**使用場景: **

  1. 需要生成的對象具有復(fù)雜的內(nèi)部結(jié)構(gòu)。
  2. 需要生成的對象內(nèi)部屬性本身相互依賴。

注意事項(xiàng):與工廠模式的區(qū)別是:建造者模式更加關(guān)注與零件裝配的順序。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言 設(shè)計(jì)模式代表了最佳的實(shí)踐,通常被有經(jīng)驗(yàn)的面向?qū)ο蟮能浖_發(fā)人員所采用。設(shè)計(jì)模式是軟件開發(fā)人員在軟件開發(fā)過程中...
    IT人生閱讀 4,008評論 0 5
  • 設(shè)計(jì)模式匯總 一、基礎(chǔ)知識 1. 設(shè)計(jì)模式概述 定義:設(shè)計(jì)模式(Design Pattern)是一套被反復(fù)使用、多...
    MinoyJet閱讀 4,094評論 1 15
  • 單例模式(SingletonPattern)一般被認(rèn)為是最簡單、最易理解的設(shè)計(jì)模式,也因?yàn)樗暮啙嵰锥琼?xiàng)目中最...
    成熱了閱讀 4,545評論 4 34
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,578評論 19 139
  • 1 單例模式的動(dòng)機(jī) 對于一個(gè)軟件系統(tǒng)的某些類而言,我們無須創(chuàng)建多個(gè)實(shí)例。舉個(gè)大家都熟知的例子——Windows任務(wù)...
    justCode_閱讀 1,558評論 2 9

友情鏈接更多精彩內(nèi)容