天天看點

設計模式之三:對象建立系列模式

前面一章《結構型系列模式》介紹了在程式架構設計中可以用到的幾種特定場景下對應的針對性模式:

1. 如針對存在大量執行個體對象的環境下存在提升性能的需求,則可以采用享元模式,以提煉出使用場景下的不變的部分,以共享來減少執行個體對象數目;

2. 對遺留的子系統想要複用,但是又不想重構子系統,則可使用外觀模式,讓一組人建構外觀類接口内部實作,另一組人則可以按照事先規定好的接口類進行編寫,兩組齊頭并進;

3. 如果核心資料流和操作流固定,但是存在大量輔助的操作函數,這些附加在資料流上的操作函數的數量和順序可以自由組裝,這時可以采用裝飾模式;

4. 如果在某些場景下,庫的作者對client想要封裝個體和團體的差別,讓客戶可以一緻性地使用單個對象群組合結構,這時則采用組合模式;

5. 如果系統UML架構規模過大過深,此時如果系統存在多角度的視角,則可以分離一個系統的不同部分,然後通過關聯關系(依賴、組合、聚合等)将各部組合起來,讓抽象和實作兩者獨立演化;

6. 如果要在目前系統中使用一個第三方庫,但是因為目前系統的接口規範已經實作規定好了,這時可采用擴充卡模式讓原本接口不相容而不能一起工作的那些類一起工作;

7. 如果想要封裝某個具體的庫對象或其他底層對象,控制不同使用者的通路權限,并且再額外添加一些其他的輔助功能如性能監測統計等,這時便可以使用代理模式。

這一章則是圍繞面向對象程式設計中的“對象”二字進行分析。我們知道在面向對象程式設計中,對象作為主要的資訊和功能載體,可以說是面向對象程式設計的核心,但是在不同場景下對象的建立需求是不一樣的,如要求某個類的執行個體對象全局有且隻有一個,如要求封裝一個同系列操作(濾波)對外提供統一表征,如client層面要使用的對象其實是有多個底層細粒度子對象組裝而成,相對client封裝組裝過程等等。下面并來看看這些不同對象建立需求場景的對應模式。

目錄:

      • 造輪子套路之一 抽象工廠模式
      • 造輪子套路之二 建造者模式
      • 造輪子套路之三 工廠方法模式
      • 造輪子套路之四 原型模式
      • 造輪子套路之五 單例模式

造輪子套路之一: 抽象工廠模式

設計模式之三:對象建立系列模式

AbstractFactory.h

#ifndef _ABSTRACTFACTORY_H
#define _ABSTRACTFACTORY_H

#include <iostream>
#include <string>

using namespace std;

class IDepartment
{
    int id;
    string depname;
public:
    virtual void Insert(IDepartment * dp)=;
    virtual IDepartment * GetDepartment(int id)=;
};

class SqlserverDepartment :public IDepartment
{
public:
    void Insert(IDepartment * dp)
    {
        cout << "在SQL Server 中給Department表增加一條記錄。\n";
    }

    IDepartment * GetDepartment(int id)
    {
        cout << "在SQL Server 中根據ID得到Department表的一條記錄。\n";
        return NULL;
    }
};


class AccessDepartment :public IDepartment
{
public:
    void Insert(IDepartment * dp)
    {
        cout << "在Access 中給Department表增加一條記錄。\n";
    }

    IDepartment * GetDepartment(int id)
    {
        cout << "在Access 中根據ID得到Department表的一條記錄。\n";
        return NULL;
    }
};


class IUser
{
public:
    virtual void Insert(IUser *) = ;
    virtual IUser * GetUser(int id) = ;
};

class SqlserverUser :public IUser
{
public:
    void Insert(IUser *)
    {
        cout << "在SQL Server中給User表增加一條記錄。\n";
    }

    IUser * GetUser(int id)
    {
        cout << "在SQL Server 中根據ID得到User表中的一條記錄。\n";
        return NULL;
    }
};

class AccessUser :public IUser
{
public:
    void Insert(IUser *)
    {
        cout << "在Access給User表增加一條記錄。\n";
    }

    IUser * GetUser(int id)
    {
        cout << "在Access中根據ID得到User表中的一條記錄。\n";
        return NULL;
    }
};


class IFactory
{
public:
    virtual IUser * CreateUser() = ;
    virtual IDepartment * CreateDepartment() = ;
};

class SqlserverFactory :public IFactory
{
public:
    IUser * CreateUser()
    {
        return new SqlserverUser;
    }

    IDepartment * CreateDepartment()
    {
        return new SqlserverDepartment;
    }
};


class AccessFactory :public IFactory
{
public:
    IUser * CreateUser()
    {
        return new AccessUser;
    }

    IDepartment * CreateDepartment()
    {
        return new AccessDepartment;
    }
};

#endif
           

test.cpp

#include "AbstractFactory.h"

int main()
{
    IUser * userI = new SqlserverUser;
    IDepartment * dptI = new SqlserverDepartment;

    IFactory * factory = new SqlserverFactory;

    IUser * user = factory->CreateUser();
    //IDepartment * dpt = factory->CreateDepartment();
    user->Insert(userI);
    user->GetUser();

    IDepartment * dpt = factory->CreateDepartment();
    dpt->Insert(dptI);
    dpt->GetDepartment();

    IDepartment * AdptI = new AccessDepartment;

    IFactory * Afactory = new AccessFactory;
    IDepartment * IDf=Afactory->CreateDepartment();
    IDf->Insert(AdptI);
    IDf->GetDepartment();

    IUser *AuserI = new AccessUser;
    IUser * AUf = Afactory->CreateUser();
    AUf->Insert(AuserI);
    AUf->GetUser();
    return ;
}
           

對象需求場景:解決多個抽象類型産品的同時建立問題,但自身代碼過于臃腫,需要搭配政策模式或反射機制使用

使用思想:封裝

評分: ★★

造輪子套路之二: 建造者模式

設計模式之三:對象建立系列模式
//car屬性類
class Car
{
public:
    void setEngine(string engine){ m_Engine = engine;}
    void setWheel(string wheel){ m_Wheel = wheel;}
    void setBody(string body){ m_Body = body;}
            void disPlay(){ }
private:
    string  m_Engine;
    string  m_Wheel;
    string  m_Body;
};
//建造者模式抽象類,組裝流程
class Assemble{
public:
    virtual void assembleEngine(){};
    virtual void assembleWheel(){};
    virtual void assembleBody(){};
    virtual Car* getCar(){ return nullptr;};
};
//a型車組裝流程
class AssembleCarA : public Assemble
{
public:
    AssembleCarA(){ _carA = new Car();}
    virtual void assembleEngine( ){ _carA->setEngine("engineA");};
    virtual void assembleWheel( ){ _carA->setWheel("whellA");}
    virtual void assembleBody( ){ _carA->setBody("bodyA");};
    virtual Car* getCar(){ return _carA; }
private:
    Car *_carA;
};
//b型車組裝流程
class AssembleCarB : public Assemble
{
public:
    AssembleCarB(){ _carB = new Car();}
    virtual void assembleEngine( ){ _carB->setEngine("engineB");};
    virtual void assembleWheel( ){ _carB->setWheel("wheelB");}
    virtual void assembleBody( ){ _carB->setBody("bodyB");};
    virtual Car* getCar(){ return _carB; }
private:
    Car *_carB;
};
//工廠類
class Director{
public:
    Director(Assemble* assemble){ m_assemble = assemble;}
    void assembleCar(){
        m_assemble->assembleEngine();
        m_assemble->assembleWheel();
        m_assemble->assembleBody();
    }
    Car* getCar(){return m_assemble->getCar();}
private:
    Assemble* m_assemble;
};
int main()
{
    Assemble *m_assemble = new AssembleCarA();
    Director *m_Director = new Director(m_assemble);
    m_Director->assembleCar();
    m_Director->getCar()->disPlay();
    return ;
}
           

對象需求場景:當需要将一個複雜對象的建造群組裝過程隐藏封裝,實作高内聚,對外僅提供一個成品,實作弱耦合

使用思想:封裝去耦合

評分: ★★

造輪子套路之三: 工廠方法模式

設計模式之三:對象建立系列模式
//simple factory model
//與簡單工廠模式的差別:簡單工廠模式在增加新操作時,需要更改工廠函數,違反了開放與封閉原則
//而工廠方法模式支援擴充,在修改時隻需要添加新的操作類和工廠類即可,但用戶端也需要作相應的修改
#include <iostream>

using namespace std;

//用一個單獨的類來做這個創造執行個體的過程,即工廠
class Operation{
public:
    Operation()
    {
        number1=;
        number2=;
        result=;
    }

    Operation(int one,int two):number1(one),number2(two)
    {
        result=;
    }

    void setOne(int one)
    {
        number1=one;
    }

    void setTwo(int two)
    {
        number2=two;
    }

    int getOne()
    {
        return number1;
    }

    int getTwo()
    {
        return number2;
    }

    virtual int getResult()
    {//虛接口,需要子類自己實作
        return result;
    }

protected:
    int number1;
    int number2;
    int result;
};

//四個具體操作的子類,主要用于實作虛函數接口
class AddOperation:public Operation{
public:
    virtual int getResult()
    {
        result=number1+number2;
        return result;
    }
};

class SubOperation:public Operation{
public:
    virtual int getResult()
    {
        result=number1-number2;
        return result;
    }
};

class MulOperation:public Operation{
public:
    virtual int getResult()
    {
        result=number1*number2;
        return result;
    }
};

class DivOperation:public Operation{
public:
    virtual int getResult()
    {
        result=number1/number2;
        return result;
    }
};

//抽象工廠函數,也是C++版的接口,不想Java等其他語言,存在單獨的關鍵詞interface來描述接口類
//C++一般通過抽象類來表示接口類,這也是為什麼C++支援多繼承,而其他語言支援單繼承+多接口實作的原因
class Factory{
public:
    virtual Operation* createOper()=;
};

//四個具體工廠
class AddFactory:public Factory{
    Operation* createOper()
    {
        AddOperation* addOper=new AddOperation();
        return addOper;
    }
};

class SubFactory:public Factory{
    Operation* createOper()
    {
        SubOperation* subOper=new SubOperation();
        return subOper;
    }
};

class MulFactory:public Factory{
    Operation* createOper()
    {
        MulOperation* mulOper=new MulOperation();
        return mulOper;
    }
};

class DivFactory:public Factory{
    Operation* createOper()
    {
        DivOperation* divOper=new DivOperation();
        return divOper;
    }
};

int main()
{
    int number1,number2;
    cout<<"please input number1:";
    cin>>number1;
    cout<<endl;

    cout<<"please input number2:";
    cin>>number2;
    cout<<endl;

    Factory* factory=new AddFactory();//建立新工廠,用于生産新的對象
    Operation* oper;//具體操作在建立時确定

    oper=factory->createOper();
    oper->setOne(number1);
    oper->setTwo(number2);

    cout<<"The Result is:";
    cout<<oper->getResult()<<endl;

    return ;
}
           

對象需求場景:看起來和抽象工廠模式很相似,但是兩者規模不一樣,抽象工廠同時要管理多個不同類型的産品,而工廠方法則隻管理一個類型的多不同系列産品的建立

使用思想:封裝去耦合

評分: ★★★

造輪子套路之四: 原型模式

設計模式之三:對象建立系列模式

Prototype.h

#ifndef _Prototype_
#define _Prototype_

#include <string>
#include <iostream>

using namespace std;

class AbsGoods{
public:
    virtual AbsGoods* Clone() = ;  //關鍵,克隆函數
    string _mName;
protected:  
    AbsGoods(const AbsGoods& another){} //拷貝構造函數
    ~AbsGoods(){} //析構函數
    AbsGoods(){} //無參構造函數

    int _Price;
};

class Mp3:public AbsGoods
{
public:
    Mp3()
    {
        cout<<"MP3 creator func"<<endl;
        _mName = "MP3"; 
        _Price = ;
    } //構造函數

    Mp3(const Mp3& another)  //拷貝構造函數
    {
        cout<<"MP3 copy func"<<endl;
        _mName = another._mName;
        _Price = another._Price;
    }

    virtual AbsGoods* Clone() //實作統一的接口克隆函數
    {
        return new Mp3(*this); //根據函數指針以及随身的虛函數指針調用MP3的拷貝構造函數
    }
};

class Computer:public AbsGoods
{
public:
    Computer()
    {
        cout<<"Computer creator func"<<endl;
        _mName = "COMPUTER"; 
        _Price = ;
    }

    Computer(const Computer& another)
    {
        cout<<"Computer copy func"<<endl;
        _mName = another._mName;
        _Price = another._Price;
    }

    virtual AbsGoods* Clone() //實作統一的接口克隆函數
    {
        return new Computer(*this);
    }
};

class Person{
public:
    Person(){_CountGoods = ;}

    bool addGoods(AbsGoods* aGoods)
    {
        if(NULL == aGoods) return false;
        if(_CountGoods >= )
            return false;
        _myGoods[_CountGoods] = aGoods;
        _CountGoods++;
        return true;
    }

    void out()
    {
        for(int i=; i<_CountGoods; i++)
        {
            cout<<_myGoods[i]->_mName<<"  ";
        }
    }

    AbsGoods* getGoods(int Num)
    {
        return _myGoods[Num-];
    }

    int getGoodsNum()
    {
        return _CountGoods;
    }

private:
    AbsGoods* _myGoods[10];
    int _CountGoods;
};

class Mical : public Person{
public:
    static Mical* getMical()
    {
        if(NULL == _thisMical)
        {
            _thisMical = new Mical;
        }
        return _thisMical;
    }
private:
    Mical(){}
    Mical(const Mical& another){}
    void operator = (const Mical& another);
    static Mical* _thisMical;  //單例模式,全局隻有一個Mical
};

class Merry : public Person{
public:
    static Merry* getMerry()
    {
        if(NULL == _thisMerry)
        {
            _thisMerry = new Merry;
        }
        return _thisMerry;
    }
private:
    Merry(){}
    Merry(const Merry& another){}
    void operator = (const Merry& another);
    static Merry* _thisMerry;//單例模式,全局隻有一個Merry
};

#endif
           

test.cpp

#include "Prototype.h"
#include <iostream>

using namespace std;

Mical* Mical::_thisMical = NULL;
Merry* Merry::_thisMerry = NULL;

int main()
{
    Mical* mical = Mical::getMical(); 
    Merry* merry = Merry::getMerry();

    mical->addGoods(new Computer); //"Computer creator func"
    mical->addGoods(new Mp3);  //"MP3 creator func"

    for(int i=; i<mical->getGoodsNum(); i++)
    {
        merry->addGoods(mical->getGoods(i+)->Clone()); //"Computer copy func" "MP3 copy func"
    }
    cout<<"Mical's Goods : ";
    mical->out(); //Mical's Goods: Computer MP3
    cout<<endl;
    cout<<"Merry's Goods : ";
    merry->out(); //"Merry's Goods: Computer MP3"
    return ;
}
           

摘錄知乎-陳碩的一句總結“Prototype 的意義在于,你拿到一個 Base* ,它指向某個 Derived 對象,你想克隆出 Derived 對象,但代碼中不寫出 Derived 的具體類型,因為有很多派生類,這種情況下你用構造函數是搞不定的,type-switch 是 bad smells 。”是以原型模式更應該叫做克隆模式,事實上,原型模式往往已經被內建在底層中,大家平時就在使用,如Java核心便已經使用原型模式配合工廠方法模式封裝對象建立細節。

對象需求場景:

1. 說白了其實就是根據對象指針反推對象具體的子類型,并再次執行個體化該子類(同類的問題還有根據函數指針反推具體的函數傳回類型、形參類型),從一個對象再建立另外一個可定制的對象,而且不需要知道任何建立的細節

2. 不想讓client知道太多具體的子類資訊,在client眼中隻需要是一個統一的抽象父類即可;

3. 要執行個體化的對象的具體子類型要在runtime才能确定(即根據傳送進來的對象指針才能知道要建立的那些具體子類);

使用思想:抽象與封裝

評分: ★★

造輪子套路之五: 單例模式

設計模式之三:對象建立系列模式
#ifndef _SINGLETON_H
#define _SINGLETON_H

#include <iostream>
#include <mutex>

class Singleton
{
private:
    Singleton(){}//私有化構造函數使得不能在外部構造執行個體,封死其他所有建立對象的入口
    static Singleton * singleton;//靜态變量
    static mutex* mtx;

public:
    /*線程不安全的傳統單例模式*/
    static Singleton * GetInstance()//提供接口建立對象
    {
        if (singleton == nullptr)//通過判斷是否有執行個體存在,如果存在就傳回該位址,這樣就保證隻有一個執行個體存在,但是實作并非線程安全
            singleton = new Singleton;
        return singleton;
    }

    /*大名鼎鼎的DCLP Doule Checked Locking Pattern*/
    static Singleton* GetInstance()
    {
        if (singleton == nullptr)
        {
            mtx->lock();
            if (singleton == nullptr)
            {
                singleton = new Singleton;
            }
            mtx->unlock();
        }
        return singleton;
    }
};

Singleton * Singleton::singleton = nullptr;//靜态類型初始化格式:<資料類型> <類名>::靜态資料名=初始化值
Singleton mutex* mtx = mutex staticLock;

#endif
           

對象需求場景:單例模式是我個人非常喜歡的一個模式,雖然使用的場景特别有限,但是确實很精巧,往往有意想不到的效果

使用思想:封裝

評分: ★★★

繼續閱讀