設計模式之:創建型設計模式(6種)


創建型設計模式有: 共6種

  • 簡單工廠模式(Simple Factory)
  • 工廠方法模式(Factory Method)
  • 抽象工廠模式(Abstract Factory)
  • 建造者模式(Builder)
  • 原型模式(Prototype)
  • 單例模式(Singleton)

 

簡單工廠模式

功能:主要用於創建對象。新添加類時,不會影響以前的系統代碼。核心思想是用一個工廠來根據輸入的條件產生不同的類,然后根據不同類的virtual函數得到不同的結果。

優點: 適用於不同情況創建不同的類時

缺點: 客戶端必須要知道基類和工廠類,耦合性差

模式結構:

  • Factory:工廠角色負責實現創建所有實例的內部邏輯
  • Product:抽象產品角色是所創建的所有對象的父類,負責描述所有實例所共有的公共接口
  • ConcreteProduct:具體產品角色是創建目標,所有創建的對象都充當這個角色的某個具體類的實例。

模式應用:

 

../_images/SimpleFactory.jpg

C++舉例:

//基類
class COperation
{
public:
    int m_nFirst;
    int m_nSecond;
    virtual double GetResult()
    {
        double dResult=0;
        return dResult;
    }
};


//加法 class AddOperation : public COperation { public: virtual double GetResult() { return m_nFirst+m_nSecond; } };

//減法 class SubOperation : public COperation { public: virtual double GetResult() { return m_nFirst-m_nSecond; } }; //工廠類 class CCalculatorFactory { public: static COperation* Create(char cOperator); //基類和工廠類是關聯關系 }; COperation* CCalculatorFactory::Create(char cOperator) { COperation *oper; switch (cOperator) { case '+': oper=new AddOperation(); break; case '-': oper=new SubOperation(); break; default: oper=new AddOperation(); break; } return oper; } //客戶端 int main() { int a,b; cin>>a>>b; COperation * op=CCalculatorFactory::Create('-'); //靜態函數直接調用 op->m_nFirst=a; op->m_nSecond=b; cout<<op->GetResult()<<endl; return 0; }

 

Java舉例:

 

 

 

 

工廠方法模式(多態工廠模式)

功能:定義一個用於創建對象的接口,讓子類決定實例化哪一個類。工廠方法使一個類的實例化延遲到其子類。

優點: 

  • 向客戶隱藏了哪種具體產品將被實例化的細節,用戶只需要關心所需產品對應的工廠,無須關心創建細節,無須知道具體產品類的類名。
  • 基於工廠角色和產品角色的多態性設計是工廠方法模式的關鍵。它能夠使工廠可以自主確定創建何種產品對象,而如何創建這個對象的細節則完全封裝在具體工廠內部。工廠方法模式之所以又被稱為多態工廠模式,是因為所有的具體工廠類都具有同一抽象父類。
  • 系統中加新產品時,無須修改抽象工廠和抽象產品的接口,(但要修改客戶端,以選擇不同的具體工廠!),無須修改其他的具體工廠和產品,而只要添加一個具體工廠和具體產品即可。符合“開閉原則”。

缺點: 

  • 在添加新產品時,需要編寫新的具體產品類,而且還要提供與之對應的具體工廠類,系統中類的個數將成對增加,一定程度上增加了系統復雜度,有更多的類需要編譯和運行,會給系統帶來一些額外的開銷。
  • 由於考慮到系統的可擴展性,需要引入抽象層,在客戶端代碼中均使用抽象層進行定義,增加了系統的抽象性和理解難度,且在實現時可能需要用到DOM、反射等技術,增加了系統的實現難度。

           

模式應用:

   JDBC中的工廠方法

 

../_images/FactoryMethod.jpg

舉例:

日志記錄器:

  某系統日志記錄器要求支持多種日志記錄方式,如文件記錄、數據庫記錄等,且用戶可以根據要求動態選擇日志記錄方式, 現使用工廠方法模式設計該系統。

../_images/loger.jpg

 

C++舉例:

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

//實例基類,相當於Product(為了方便,沒用抽象)
class LeiFeng
{
public:
    virtual void Sweep()
    {
        cout<<"雷鋒掃地"<<endl;
    }
};

//學雷鋒的大學生,相當於ConcreteProduct(具體產品1)
class Student: public LeiFeng
{
public:
    virtual void Sweep()
    {
        cout<<"大學生掃地"<<endl;
    }
};

//學雷鋒的志願者,相當於ConcreteProduct(具體產品2)
class Volenter: public LeiFeng
{
public :
    virtual void Sweep()
    {
        cout<<"志願者"<<endl;
    }
};

-----------------------------
//工場基類Creator
class LeiFengFactory
{
public:
    virtual LeiFeng* CreateLeiFeng() //返回值體現依賴關系
    {
        return new LeiFeng();
    }
};

//工場具體類1(創建大學生產品的具體工廠)
class StudentFactory : public LeiFengFactory
{
public :
    virtual LeiFeng* CreateLeiFeng() //返回值體現依賴關系
    {
        return new Student();
    }
};

//工場具體類2(創建志願者產品的具體工廠)
class VolenterFactory : public LeiFengFactory
{
public:
    virtual LeiFeng* CreateLeiFeng()
    {
        return new Volenter(); //返回值體現依賴關系
    }
};

-----------------------------
//客戶端
int main()
{
    LeiFengFactory *sf=new StudentFactory(); //由客戶端選創建什么產品
    LeiFeng *s=sf->CreateLeiFeng();
    s->Sweep();
    delete s;
    delete sf;
    return 0;
}

 

Java舉例:

 

抽象工廠模式(Kit模式)

功能:提供一個創建一系列相關或相互依賴對象的接口,而無需指定它們具體的類。

優點: 

  • 抽象工廠模式隔離了具體類的生成,使得客戶並不需要知道什么被創建。由於這種隔離,更換一個具體工廠就變得容易。所有的具體工廠都實現了抽象工廠中定義的那些公共接口,因此只需改變具體工廠的實例,就可以在某種程度上改變整個軟件系統的行為。另外,此模式可以實現高內聚低耦合的設計目的。
  • 當一個產品族中的多個對象被設計成一起工作時,它能夠保證客戶端始終只使用同一個產品族中的對象。這對一些需要根據當前環境來決定其行為的軟件系統來說,很實用。
  • 增加新的具體工廠和產品族很方便,無須修改已有系統,符合“開閉原則”。

缺點: 

  • 在添加新的產品對象時,難以擴展抽象工廠來生產新種類的產品,這是因為在抽象工廠角色中規定了所有可能被創建的產品集合,要支持新種類的產品就意味着要對該接口進行擴展,而這將涉及到對抽象工廠角色及其所有子類的修改,顯然會帶來較大的不便。
  • 開閉原則的傾斜性(增加新的工廠和產品族容易,增加新的產品等級結構麻煩)。

模式應用:

  在很多軟件系統中需要更換界面主題,要求界面中的按鈕、文本框、背景色等一起發生改變時,可以使用抽象工廠模式進行設計。

 

../_images/AbatractFactory.jpg

 

C++舉例:

#include <string>
#include <iostream>
#include <vector>
using namespace std;

//用戶抽象接口(抽象產品A)
class IUser
{
public :
    virtual void GetUser()=0;
    virtual void InsertUser()=0;
};

//部門抽象接口(抽象產品B)
class IDepartment
{
public:
    virtual void GetDepartment()=0;
    virtual void InsertDepartment()=0;
};

//ACCESS用戶(抽象產品A的產品1)
class CAccessUser : public IUser
{
public:
    virtual void GetUser()
    {
        cout<<"Access GetUser"<<endl;
    }
    virtual void InsertUser()
    {
        cout<<"Access InsertUser"<<endl;
    }
};

//ACCESS部門(抽象產品B的產品1)
class CAccessDepartment : public IDepartment
{
public:
    virtual void GetDepartment()
    {
        cout<<"Access GetDepartment"<<endl;
    }
    virtual void InsertDepartment()
    {
        cout<<"Access InsertDepartment"<<endl;
    }
};

//SQL用戶(抽象產品A的產品2)
class CSqlUser : public IUser
{
public:
    virtual void GetUser()
    {
        cout<<"Sql User"<<endl;
    }
    virtual void InsertUser()
    {
        cout<<"Sql User"<<endl;
    }
};

//SQL部門類(抽象產品B的產品2)
class CSqlDepartment: public IDepartment
{
public:
    virtual void GetDepartment()
    {
        cout<<"sql getDepartment"<<endl;
    }
    virtual void InsertDepartment()
    {
        cout<<"sql insertdepartment"<<endl;
    }
};

//抽象工廠
class IFactory
{
public:
    virtual IUser* CreateUser()=0;
    virtual IDepartment* CreateDepartment()=0;
};

//ACCESS工廠
class AccessFactory : public IFactory
{
public:
    virtual IUser* CreateUser()
    {
        return new  CAccessUser();
    }
    virtual IDepartment* CreateDepartment()
    {
        return new CAccessDepartment();
    }
};

//SQL工廠
class SqlFactory : public IFactory
{
public:
    virtual IUser* CreateUser()
    {
        return new  CSqlUser();
    }

    virtual IDepartment* CreateDepartment()
    {
        return new CSqlDepartment();
    }
};

//客戶端:
int main()
{
    IFactory* factory= new SqlFactory();
    IUser* user=factory->CreateUser();
    IDepartment* depart = factory->CreateDepartment();

    user->GetUser();
    depart->GetDepartment();
    return 0;
}

 

Java舉例:

 

建造者模式(生成器模式)

功能:將一個復雜對象的構建表示分離,使得同樣的構建過程可以創建不同的表示。

優點: 

  • 客戶端不必知道產品內部組成的細節,將產品本身與產品的創建過程解耦,使得相同的創建過程可以創建不同的產品對象。
  • 每一個具體建造者都獨立,因此可以方便地替換具體建造者或增加新的具體建造者, 用戶使用不同的具體建造者即可得到不同的產品對象 。
  • 可以更加精細地控制產品的創建過程 。將復雜產品的創建步驟分解在不同的方法中,使得創建過程更加清晰,也更方便使用程序來控制創建過程。
  • 增加新的具體建造者無須修改原有類庫的代碼,指揮者類針對抽象建造者類編程,系統擴展方便,符合“開閉”

缺點: 

  • 建造者模式所創建的產品一般具有較多的共同點,其組成部分相似,若產品之間的差異性很大,則不適合使用該模式,因此其使用范圍受到一定限制。
  • 若產品的內部變化復雜,可能會導致需要定義很多具體建造者類來實現這種變化,導致系統變得很龐大。

模式應用:

  很多游戲軟件中,地圖包括天空、地面、背景等組成部分,人物角色包括人體、服裝、裝備等組成部分,可以使用建造者模式對其進行設計,通過不同的具體建造者創建不同類型的地圖或人物。

../_images/Builder.jpg

 

舉例: KFC套餐

套餐是一個復雜對象,一般包含主食(如漢堡、雞肉卷等)和飲料(如果汁、 可樂等)等部分,不同的套餐有不同的組成部分,而KFC的服務員可以根據顧客的要求,一步一步裝配這些組成部分,構造一份完整的套餐,然后返回給顧客。

../_images/KFCBuilder.jpg

 

C++舉例:

#include <string>
#include <iostream>
#include <vector>
using namespace std;

//最終的產品類
class Product 
{
private:
    vector<string> m_product;
public:
    void Add(string strtemp)
    {
        m_product.push_back(strtemp);
    }
    void Show()
    {
        vector<string>::iterator p=m_product.begin();
        while (p!=m_product.end())
        {
            cout<<*p<<endl;
            p++;
        }
    }
};

//建造者基類
class Builder
{
public:
    virtual void BuilderA()=0;
    virtual void BuilderB()=0;
    virtual Product* GetResult()=0;
};

//第一種建造方式
class ConcreteBuilder1 : public Builder
{
private:
    Product* m_product; //體現依賴關系
public:
    ConcreteBuilder1()
    {
        m_product=new Product();
    }
    virtual void BuilderA()
    {
        m_product->Add("one");
    }
    virtual void BuilderB()
    {
        m_product->Add("two");
    }
    virtual Product* GetResult()
    {
        return m_product;
    }
};

//第二種建造方式
class ConcreteBuilder2 : public Builder
{
private:
    Product * m_product;  //體現依賴關系
public:
    ConcreteBuilder2()
    {
        m_product=new Product();
    }
    virtual void BuilderA()
    {
        m_product->Add("A");
    }
    virtual void BuilderB()
    {
        m_product->Add("B");
    }
    virtual Product* GetResult()
    {
        return m_product;
    }
};

//指揮者類
class Direct
{
public:
    void Construct(Builder* temp) //構建一個使用Builder接口的對象
    {
        temp->BuilderA();
        temp->BuilderB();
    }
};

//客戶端
int main()
{
    Direct *p=new Direct();
    Builder* b1=new ConcreteBuilder1();
    Builder* b2=new ConcreteBuilder2();

    p->Construct(b1);                 //調用第一種建造方式
    Product* pb1 = b1->GetResult();
    pb1->Show();

    p->Construct(b2);                 //調用第二種建造方式
    Product * pb2 = b2->GetResult();
    pb2->Show();
    return 0;
}

 

單例模式

功能:保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。

優點: 

  • 提供了對唯一實例的受控訪問。因為單例類封裝了它的唯一實例,所以它可以嚴格控制客戶怎樣以及何時訪問它,並為設計及開發團隊提供了共享的概念。
  • 由於在系統內存中只存在一個對象,因此可以節約系統資源,對於一些需要頻繁創建和銷毀的對象,單例模式無疑可以提高系統的性能。
  • 允許可變數目的實例。我們可以基於單例模式進行擴展,使用與單例控制相似的方法來獲得指定個數的對象實例。

缺點: 

  • 由於單例模式中沒有抽象層,因此單例類的擴展有很大的困難。
  • 單例類的職責過重,在一定程度上違背了“單一職責原則”。因為單例類既充當了工廠角色,提供了工廠方法,同時又充當了產品角色,包含一些業務方法,將產品的創建和產品的本身的功能融合到一起。
  • 濫用單例將帶來一些負面問題,如為了節省資源將數據庫連接池對象設計為單例類,可能會導致共享連接池對象的程序過多而出現連接池溢出;現在很多面向對象語言(如Java、C#)的運行環境都提供了自動垃圾回收的技術,因此,如果實例化的對象長時間不被利用,系統會認為它是垃圾,會自動銷毀並回收資源,下次利用時又將重新實例化,這將導致對象狀態的丟失。

模式應用:

  一個具有自動編號主鍵的表可以有多個用戶同時使用,但數據庫中只能有一個地方分配下一個主鍵編號,否則會出現主鍵重復,因此該主鍵編號生成器必須具備唯一性,可以通過單例模式來實現。

../_images/Singleton.jpg

舉例:

  在OS中,打印池(Print Spooler)是一個用於管理打印任務的應用程序,通過打印池用戶可以刪除、中止或者改變打印任務的優先級,在一個系統中只允許運行一個打印池對象,如果重復創建打印池則拋出異常。現使用單例模式來模擬實現打印池的設計。

 

C++舉例:

懶漢式單例:

#include <iostream>
#include <string>
#include <vector>
using namespace std;

class Singelton
{
private:
    Singelton(){ }  //構造函數私有堵死了外界創建副本的可能!!!
    static Singelton* singel; //私有的靜態全局變量來保存該類的唯一實例

public:
    static Singelton* GetInstance() //獲得本類實例的唯一全局訪問點
    {
        if(singel == NULL)
        {
            singel = new Singelton();
        }
        return singel;
    }

};
Singelton* Singelton::singel = NULL;//注意靜態變量類外初始化

//客戶端:
int main()
{
    Singelton* s1=Singelton::GetInstance();
    Singelton* s2=Singelton::GetInstance();
    if(s1 == s2)  //比較兩次實例化后的結果是實例相同!!!
        cout<<"ok"<<endl;
    else
        cout<<"no"<<endl;
    return 0;
}

 

 

Java舉例:

 

 

原型模式

功能:用原型實例指定創建對象的種類,並通過拷貝這些原型創建新的對象。原型模式其實就是從一個對象創建另外一個可定制的對象,而且不需知道任何創建的細節。

優點:   

  一般在初始化的信息不發生變化的情況下,克隆是最好的辦法,既隱藏了對象創建細節,又提高性能。其等於是不用重新初始化對象,而是動態地獲得對象運行時的狀態。

缺點: 

 

模式應用:

 

 

C++舉例

#include<iostream>
#include <vector>
#include <string>
using namespace std;

//抽象基類
class Prototype  
{ 
private:
    string m_strName;

public: 
    Prototype(string strName){ m_strName = strName; }
    Prototype() { m_strName = " "; }
    void Show() 
    {
        cout<<m_strName<<endl;
    }
    virtual Prototype* Clone() = 0 ; //關鍵就在於這樣一個抽象方法
} ; 

// class ConcretePrototype1 
class ConcretePrototype1 : public Prototype 
{ 
public: 
    ConcretePrototype1(string strName) : Prototype(strName){}
    ConcretePrototype1(){}

    virtual Prototype* Clone() 
    { 
        ConcretePrototype1 *p = new ConcretePrototype1() ; 
        *p = *this ;                         //復制對象 
        return p ; 
    } 
} ; 

// class ConcretePrototype2 
class ConcretePrototype2 : public Prototype 
{ 
public: 
    ConcretePrototype2(string strName) : Prototype(strName){}
    ConcretePrototype2(){}

    virtual Prototype* Clone() 
    { 
        ConcretePrototype2 *p = new ConcretePrototype2() ; 
        *p = *this ; //復制對象 
        return p ; 
    } 
} ; 


//客戶端
int main()
{
    ConcretePrototype1* test = new ConcretePrototype1("小王");
    ConcretePrototype2* test2 = (ConcretePrototype2*)test->Clone();
    test->Show();
    test2->Show();
    return 0;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM