C++設計模式 | 四種創建型模式——簡單工廠模式、工廠方法模式、抽象工廠模式、單例模式

簡單工廠模式

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;


//抽象水果
class Fruit{
public:
    virtual void shoName() = 0;
};

//蘋果類
class Apple : public Fruit{
public:
    virtual void shoName(){
        cout << "我是蘋果" << endl;
    }
};

//香蕉類
class Banana : public Fruit{
public:
    virtual void shoName(){
        cout << "我是香蕉" << endl;
    }
};

//鴨梨類
class Pear : public Fruit{
public:
    virtual void shoName(){
        cout << "我是鴨梨" << endl;
    }
};

//水果工廠
class FruitFactory{
public:
    static Fruit* CreateFruit(string name){
        if (name.compare("apple") == 0){
            return new Apple;
        }
        else if (name.compare("banana") == 0){
            return new Banana;
        }
        else if (name.compare("pear") == 0){
            return new Pear;
        }
    }
};

//測試
void test01(){
    
    Fruit* fruit = NULL;
    fruit = FruitFactory::CreateFruit("apple");  //工廠生產蘋果
    fruit->shoName();
    delete fruit;

    fruit = FruitFactory::CreateFruit("banana"); //工廠生產香蕉
    fruit->shoName();
    delete fruit;

    fruit = FruitFactory::CreateFruit("pear"); //工廠生產鴨梨
    fruit->shoName();
    delete fruit;

}

int main(){

    test01();

    
    return EXIT_SUCCESS;
}
簡單工廠模式的優缺點及適用場景

優點:
(1)實現了對象創建和使用的分離。
(2)不需要記住具體類名,記住參數即可,減少使用者記憶量。

缺點:
(1)對工廠類職責過重,一旦不能工作,系統受到影響。
(2)增加系統中類的個數,復雜度和理解度增加。
(3)違反“開閉原則”,添加新產品需要修改工廠邏輯,工廠越來越復雜。

適用場景:

  1. 工廠類負責創建的對象比較少,由于創建的對象較少,不會造成工廠方法中的業務邏輯太過復雜。
  2. 客戶端只知道傳入工廠類的參數,對于如何創建對象并不關心。

工廠方法模式

工廠方法(Factory Method)模式的意義是定義一個創建產品對象的工廠接口,將實際創建工作推遲到子類當中。核心工廠類不再負責產品的創建,這樣核心類成為一個抽象工廠角色,僅負責具體工廠子類必須實現的接口,這樣進一步抽象化的好處是使得工廠方法模式可以使系統在不修改具體工廠角色的情況下引進新的產品。

工廠方法模式是簡單工廠模式的衍生,解決了許多簡單工廠模式的問題。
首先完全實現‘開閉原則’,實現了可擴展。

工廠方法模式中的角色與職責:
抽象工廠(Abstract Factory)角色:工廠方法模式的核心,任何工廠類都必須實現這個接口。
工廠(Concrete Factory)角色:具體工廠類是抽象工廠的一個實現,負責實例化產品對象。
抽象產品(Abstract Product)角色:工廠方法模式所創建的所有對象的父類,它負責描述所有實例所共有的公共接口。
具體產品(Concrete Product)角色:工廠方法模式所創建的具體實例對象。

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;


//抽象水果
class AbstractFruit{
public:
    virtual void showName() = 0;
};

/* 具體水果 start  */

//蘋果
class Apple : public AbstractFruit{
public:
    virtual void showName(){
        cout << "我是蘋果" << endl;
    }
};

//香蕉
class Banana : public AbstractFruit{
public:
    virtual void showName(){
        cout << "我是香蕉" << endl;
    }
};

//鴨梨
class Pear : public AbstractFruit{
public:
    virtual void showName(){
        cout << "我是鴨梨" << endl;
    }
};

/* 具體水果 end  */

//抽象工廠
class AbstractFactory{
public:
    virtual AbstractFruit* CreateFruit() = 0;
};

/* 具體工廠類 start */

//蘋果工廠
class AppleFactory : public AbstractFactory{
public:
    virtual AbstractFruit* CreateFruit(){
        return new Apple;
    }
};

//香蕉工廠
class BananaFactory : public AbstractFactory{
public:
    virtual AbstractFruit* CreateFruit(){
        return new Banana;
    }
};

//鴨梨工廠
class PearFactory : public AbstractFactory{
public:
    virtual AbstractFruit* CreateFruit(){
        return new Pear;
    }
};

/* 具體工廠類 end */


//測試
void test01(){
    
    AbstractFactory* factory = NULL;
    AbstractFruit* fruit = NULL;

    factory = new AppleFactory; //創建蘋果工廠
    fruit = factory->CreateFruit(); //蘋果工廠生產蘋果
    fruit->showName();

    factory = new BananaFactory; //創建香蕉工廠
    fruit = factory->CreateFruit(); //香蕉工廠生產蘋果
    fruit->showName();

    factory = new PearFactory; //創建鴨梨工廠
    fruit = factory->CreateFruit(); //鴨梨工廠生產蘋果
    fruit->showName();

}

int main(){

    test01();

    
    return EXIT_SUCCESS;
}
工廠方法模式的優缺點及適用場景

優點:
(1)不需要記住具體類名,甚至連具體參數都不用記憶。
(2)實現了對象創建和使用的分離。
(3)系統的可擴展性也就變得非常好,無需修改接口和原類。

缺點:
(1)增加系統中類的個數,復雜度和理解度增加。
(2)增加了系統的抽象性和理解難度。

適用場景:

  1. 客戶端不知道它所需要的對象的類。
  2. 抽象工廠類通過其子類來指定創建哪個對象。

抽象工廠模式

工廠方法模式通過引入工廠等級結構,解決了簡單工廠模式中工廠類職責太重的問題,但由于工廠方法模式中的每個工廠只生產一類產品,可能會導致系統中存在大量的工廠類,勢必會增加系統的開銷。此時,我們可以考慮將一些相關的產品組成一個“產品族,由同一個工廠來統一生產,這就是我們本文將要學習的抽象工廠模式的基本思想。

模式中的角色和職責:

抽象工廠(Abstract Factory)角色:它聲明了一組用于創建一族產品的方法,每一個方法對應一種產品。
具體工廠(Concrete Factory)角色:它實現了在抽象工廠中聲明的創建產品的方法,生成一組具體產品,這些產品構成了一個產品族,每一個產品都位于某個產品等級結構中。
抽象產品(Abstract Product)角色:它為每種產品聲明接口,在抽象產品中聲明了產品所具有的業務方法。
具體產品(Concrete Product)角色:它定義具體工廠生產的具體產品對象,實現抽象產品接口中聲明的業務方法。

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

//抽象蘋果類
class AbstractApple{
public:
    virtual void showName() = 0;
};

//抽象香蕉
class AbstractBanana{
public:
    virtual void showName() = 0;
};

//抽象鴨梨
class AbstractPear{
public:
    virtual void showName() = 0;
};

//中國蘋果
class ChineseApple : public AbstractApple{
public:
    virtual void showName(){
        cout << "中國蘋果" << endl;
    }
};

//美國蘋果
class AmericanApple : public AbstractApple{
public:
    virtual void showName(){
        cout << "美國蘋果" << endl;
    }
};

//日本蘋果
class JapaneseApple : public AbstractApple{
public:
    virtual void showName(){
        cout << "日本蘋果" << endl;
    }
};

//中國香蕉
class ChineseBanana : public AbstractBanana{
public:
    virtual void showName(){
        cout << "中國香蕉" << endl;
    }
};

//美國香蕉
class AmericanBanana : public AbstractBanana{
public:
    virtual void showName(){
        cout << "美國香蕉" << endl;
    }
};

//日本香蕉
class JapaneseBanana : public AbstractBanana{
public:
    virtual void showName(){
        cout << "日本香蕉" << endl;
    }
};

//中國鴨梨
class ChinesePear : public AbstractPear{
public:
    virtual void showName(){
        cout << "中國鴨梨" << endl;
    }
};

//美國鴨梨
class AmericanPear : public AbstractPear{
public:
    virtual void showName(){
        cout << "美國鴨梨" << endl;
    }
};

//日本鴨梨
class JapanesePear : public AbstractPear{
public:
    virtual void showName(){
        cout << "日本鴨梨" << endl;
    }
};

//抽象工廠
class AbstractFactory{
public:
    virtual AbstractApple* CreateApple() = 0;
    virtual AbstractBanana* CreateBanana() = 0;
    virtual AbstractPear* CreatePear() = 0;
};

//中國工廠
class ChineseFactory : public AbstractFactory{
public:
    virtual AbstractApple* CreateApple(){
        return new ChineseApple;
    }
    virtual AbstractBanana* CreateBanana(){
        return new ChineseBanana;
    }
    virtual AbstractPear* CreatePear(){
        return new ChinesePear;
    }
};

//美國工廠
class AmericanFactory : public AbstractFactory{
public:
    virtual AbstractApple* CreateApple(){
        return new AmericanApple;
    }
    virtual AbstractBanana* CreateBanana(){
        return new AmericanBanana;
    }
    virtual AbstractPear* CreatePear(){
        return new AmericanPear;
    }
};

//美國工廠
class JapaneseFactory : public AbstractFactory{
public:
    virtual AbstractApple* CreateApple(){
        return new JapaneseApple;
    }
    virtual AbstractBanana* CreateBanana(){
        return new JapaneseBanana;
    }
    virtual AbstractPear* CreatePear(){
        return new JapanesePear;
    }
};


void test01(){
    
    AbstractFactory* factory = NULL;
    AbstractApple* apple = NULL;
    AbstractBanana* banana = NULL;
    AbstractPear* pear = NULL;

    factory = new ChineseFactory; //創建中國工廠
    apple = factory->CreateApple();
    banana = factory->CreateBanana();
    pear = factory->CreatePear();

    apple->showName();
    banana->showName();
    pear->showName();

    delete pear;
    delete banana;
    delete apple;
    delete factory;

    factory = new AmericanFactory; //創建美國工廠
    apple = factory->CreateApple();
    banana = factory->CreateBanana();
    pear = factory->CreatePear();

    apple->showName();
    banana->showName();
    pear->showName();

    delete pear;
    delete banana;
    delete apple;
    delete factory;

    factory = new JapaneseFactory; //創建日本工廠
    apple = factory->CreateApple();
    banana = factory->CreateBanana();
    pear = factory->CreatePear();

    apple->showName();
    banana->showName();
    pear->showName();

    delete pear;
    delete banana;
    delete apple;
    delete factory;

}

int main(){

    test01();


    return EXIT_SUCCESS;
}
抽象工廠模式的優缺點及適用場景

優點:
(1)擁有工廠方法模式的優點
(2)當一個產品族中的多個對象被設計成一起工作時,它能夠保證客戶端始終只使用同一個產品族中的對象。
(3)增加新的產品族很方便,無須修改已有系統,符合“開閉原則”。

缺點:
增加新的產品等級結構麻煩,需要對原有系統進行較大的修改,甚至需要修改抽象層代碼,這顯然會帶來較大的不便,違背了“開閉原則”。

適用場景:
(1) 系統中有多于一個的產品族。而每次只使用其中某一產品族。可以通過配置文件等方式來使得用戶可以動態改變產品族,也可以很方便地增加新的產品族。
(2) 產品等級結構穩定。設計完成之后,不會向系統中增加新的產品等級結構或者刪除已有的產品等級結構。

單例模式

單例模式是一種常用的軟件設計模式。在它的核心結構中只包含一個被稱為單例的特殊類。通過單例模式可以保證系統中一個類只有一個實例而且該實例易于外界訪問,從而方便對實例個數的控制并節約系統資源。如果希望在系統中某個類的對象只能存在一個,單例模式是最好的解決方案。

Singleton(單例):在單例類的內部實現只生成一個實例,同時它提供一個靜態的getInstance()工廠方法,讓客戶可以訪問它的唯一實例;為了防止在外部對其實例化,將其構造函數設計為私有;在單例類內部定義了一個Singleton類型的靜態對象,作為外部共享的唯一實例。

(重點)
如何構建單例:
一是單例模式的類只提供私有的構造函數,
二是類定義中含有一個該類的靜態私有對象,
三是該類提供了一個靜態的公有的函數用于創建或獲取它本身的靜態私有對象。

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

/* 懶漢式 */
class Chairman_lazy{
private:
    Chairman_lazy(){}
public:
    static Chairman_lazy* getInstance(){
        if (s_singleton == NULL){
            s_singleton = new Chairman_lazy;
        }
        return s_singleton;
    }
private:
    static Chairman_lazy* s_singleton;
};

Chairman_lazy* Chairman_lazy::s_singleton = NULL;

void test01(){
    
    Chairman_lazy* chairman1 = Chairman_lazy::getInstance();
    Chairman_lazy* chairman2 = Chairman_lazy::getInstance();
    if (chairman1 == chairman2){
        cout << "指向同一個對象!" << endl;
    }
    else{
        cout << "指向不是同一個對象!" << endl;
    }

}

/* 餓漢式 */
class Chairman_hangry{
private:
    Chairman_hangry(){}
public:
    static Chairman_hangry* getInstance(){
        return s_singleton;
    }
private:
    static Chairman_hangry* s_singleton;
};

//初始化
Chairman_hangry* Chairman_hangry::s_singleton = new Chairman_hangry;

void test02(){
    
    Chairman_hangry* chairman1 = Chairman_hangry::getInstance();
    Chairman_hangry* chairman2 = Chairman_hangry::getInstance();
    if (chairman1 == chairman2){
        cout << "指向同一個對象!" << endl;
    }
    else{
        cout << "指向不是同一個對象!" << endl;
    }
}

int main(){

    //test01();
    test02();

    
    return EXIT_SUCCESS;
}
單例模式遇到多線程時
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<Windows.h>
using namespace std;

/* 懶漢式 */
class Chairman_lazy{
private:
    Chairman_lazy(){}
public:
    static Chairman_lazy* getInstance(){
        if (s_singleton == NULL){
            //Sleep(1000); //等到1000秒
            s_singleton = new Chairman_lazy;
        }
        return s_singleton;
    }
private:
    static Chairman_lazy* s_singleton;
};

Chairman_lazy* Chairman_lazy::s_singleton = NULL;


/* 餓漢式 */
class Chairman_hangry{
private:
    Chairman_hangry(){}
public:
    static Chairman_hangry* getInstance(){
        return s_singleton;
    }
private:
    static Chairman_hangry* s_singleton;
};

//初始化
Chairman_hangry* Chairman_hangry::s_singleton = new Chairman_hangry;

DWORD WINAPI MyThread_hangry(LPVOID lpThreadParameter){
    
    Chairman_hangry* chairman = Chairman_hangry::getInstance();
    cout << "單例對象地址:" << (int*)chairman << endl;

    return 0;
}

//餓漢式單例碰到多線程測試
void test01(){

    HANDLE handler[10];
    for (int i = 0; i < 10;i++){
        handler[i] = CreateThread(NULL, NULL, MyThread_hangry, NULL, NULL, NULL);
    }

}

DWORD WINAPI MyThread_lazy(LPVOID lpThreadParameter){

    Chairman_lazy* chairman = Chairman_lazy::getInstance();
    cout << "單例對象地址:" << (int*)chairman << endl;

    return 0;
}

//懶漢式單例碰到多線程
void test02(){

    HANDLE handler[10];
    for (int i = 0; i < 10; i++){
        handler[i] = CreateThread(NULL, NULL, MyThread_lazy, NULL, NULL, NULL);
    }

}

int main(){

    test01();
    test02();
    return EXIT_SUCCESS;
}

運行結果:
餓漢式單例模式的單例對象地址是全部一致的
懶漢式單例模式的單例對象地址都不一致

由上面的結果可知,懶漢式單例模式下的多線程是不安全的。
原因:懶漢式的getInstance語句是這樣的:

static Chairman_lazy* getInstance(){
        if (s_singleton == NULL){
            //Sleep(1000); //等到1000秒
            s_singleton = new Chairman_lazy;
        }
        return s_singleton;
    }

假如多個Chairman_lazy* Chairman_lazy::s_singleton = NULL;語句
因為多線程下這些語句會同時執行,那么會同時new很多實例,所以不安全

相反,餓漢模式只會在全局new一個實例,getInstance語句是直接返回指針,所以安全。

//線程安全的懶漢模式
class singleton
{
protected:
    singleton()
    {
        pthread_mutex_init(&mutex);
    }
private:
    static singleton* p;
public:
    static pthread_mutex_t mutex;
    static singleton* initance();
};
 
pthread_mutex_t singleton::mutex;
singleton* singleton::p = NULL;
singleton* singleton::initance()
{
    if (p == NULL)
    {
        pthread_mutex_lock(&mutex);
        if (p == NULL)
            p = new singleton();
        pthread_mutex_unlock(&mutex);
    }
    return p;
}

單例模式的優缺點及適用場景

優點:
(1)單例模式提供了對唯一實例的受控訪問。
(2)節約系統資源。由于在系統內存中只存在一個對象。

缺點:
(1) 擴展略難。單例模式中沒有抽象層。
(2) 單例類的職責過重。

適用場景:
(1) 系統只需要一個實例對象,如系統要求提供一個唯一的序列號生成器或資源管理器,或者需要考慮資源消耗太大而只允許創建一個對象。
(2) 客戶調用類的單個實例只允許使用一個公共訪問點,除了該公共訪問點,不能通過其他途徑訪問該實例。

關于單例對象內存的釋放問題

一般一個單例對象占用的內存很少,就算不釋放內存也沒關系
如果真的想是釋放,可以用嵌套類來實現

最重要的一點,之所以不寫析構函數,是因為單例模式的實例是存儲在堆中,不會自動析構,所以需要借助內嵌類delete觸發。

class Chairman_hangry{
private:
    Chairman_hangry(){}
        ~Chairman_hangry(){}
public:
    static Chairman_hangry* getInstance(){
        return s_singleton;
    }
class Garbo{
  public:
  ~Garbo(){
    if(s_singleton!=NULL)
        delete s_singleton;
  }
};

private:
    static Chairman_hangry* s_singleton;
        static Garbo garbo;
};
Chairman_hangry* Chairman_hangry::s_singleton=new Chairman_hangry;
Chairman_hangry::Garbo Chairman_hangry::garbo; //這句很重要
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容