C++設計模式——簡單工廠模式與策略模式比較


簡單工廠模式本應該放到工廠模式那篇博客中去介紹的,因為與策略模式有一定的相似性,這里摘出來單獨成章。

簡單工廠模式

用一個單獨的類來做創造實例的過程,就是工廠。

工廠模式有一種非常形象的描述:建立對象的類就如一個工廠,而需要被建立的對象就是一個個產品;在工廠中加工產品,使用產品的人,不用在乎產品是如何生產出來的。從軟件開發的角度來說,這樣就有效的降低了模塊之間的耦合。

適用場合

在程序中,需要創建的對象很多,導致對象的new操作多且雜時,需要使用簡單工廠模式;
由於對象的創建過程是我們不需要去關心的,而我們注重的是對象的實際操作,所以,我們需要分離對象的創建和操作兩部分,如此,方便后期的程序擴展和維護。

基本代碼

typedef enum ProductTypeTag
{
    TypeA,
    TypeB,
    TypeC
} PRODUCTTYPE;

class Product
{
public:
    virtual void Show() = 0;
};

class ProductA : public Product
{
public:
    void Show()
    {
        cout<<"I'm ProductA"<<endl;
    }
};
class ProductB : public Product
{
public:
    void Show()
    {
        cout<<"I'm ProductB"<<endl;
    }
};

class ProductC : public Product
{
public:
    void Show()
    {
        cout<<"I'm ProductC"<<endl;
    }
};
class Factory
{
public:
    Product* CreateProduct(PRODUCTTYPE type)
    {
        switch (type)
        {
        case TypeA:
            return new ProductA();
        case TypeB:
            return new ProductB();
        case TypeC:
            return new ProductC();
        default:
            return NULL;
        }
    }
};
int main(int argc, char *argv[])
{
    Factory *ProductFactory = new Factory();
    Product *productObjA = ProductFactory->CreateProduct(TypeA);
    if (productObjA != NULL)
        productObjA->Show();
    Product *productObjB = ProductFactory->CreateProduct(TypeB);
    if (productObjB != NULL)
        productObjB->Show();
    Product *productObjC = ProductFactory->CreateProduct(TypeC);
    if (productObjC != NULL)
        productObjC->Show();
    return 0;
}

簡單工廠模式的局限性

若工廠現在能生產ProductA、ProductB和ProductC三種產品了,此時,需要增加生產ProductD產品;那么,首先是不是需要在產品枚舉類型中添加新的產品類型標識,然后,修改Factory類中的switch結構代碼。是的,這種對代碼的修改,對原有代碼的改動量較大,易產生編碼上的錯誤(雖然很簡單,如果工程大了,出錯也是在所難免的!!!)。同時,由於對已經存在的函數進行了修改,那么以前進行過的測試,都將是無效的,所有的測試,都將需要重新進行,所有的代碼都需要進行重新覆蓋。

工廠模式的引入

 

FactoryA專心負責生產ProductA,FactoryB專心負責生產ProductB,FactoryA和FactoryB之間沒有關系;如果到了后期,如果需要生產ProductC時,我們則可以創建一個FactoryC工廠類,該類專心負責生產ProductC類產品。由於FactoryA、FactoryB和FactoryC之間沒有關系,當加入FactoryC加入時,對FactoryA和FactoryB的工作沒有產生任何影響,那么對代碼進行測試時,只需要單獨對FactoryC和ProductC進行單元測試,而FactoryA和FactoryB則不用進行測試,可省去大量無趣無味的測試工作。

class Product
{
public:
    virtual void Show() = 0;
};

class ProductA : public Product
{
public:
    void Show()
    {
        cout<< "I'm ProductA"<<endl;
    }
};
class ProductB : public Product
{
public:
    void Show()
    {
        cout<< "I'm ProductB"<<endl;
    }
};
class Factory
{
public:
    virtual Product *CreateProduct() = 0;
};
class FactoryA : public Factory
{
public:
    Product *CreateProduct()
    {
        return new ProductA ();
    }
};
class FactoryB : public Factory
{
public:
    Product *CreateProduct()
    {
        return new ProductB ();
    }
};
int main(int argc, char *argv [])
{
    Factory *factoryA = new FactoryA ();
    Product *productA = factoryA->CreateProduct();
    productA->Show();
    Factory *factoryB = new FactoryB ();
    Product *productB = factoryB->CreateProduct();
    productB->Show();

    return 0;
}

 詳細內容參見上一篇博客C++設計模式——工廠模式Factory Method

 

簡單工廠模式和策略模式的比較

看到這個UML圖回想一下 之前學習過的策略模式

二者好像差不多?

 

唯一不同的就是 簡單工廠類 和 Context類。

原博客是用Java實現的,由於並不影響閱讀與理解,在這里直接拷貝過來,沒有做修改
 
簡單工廠類和Context類中代碼的區別

簡單工廠類:
public class OperationFactory
{
    public static Operation CreateOperate (string operate)
    {
        Operation oper=null;   
        switch (operate)
        {
            case "+":
                oper = new OperationAdd();
                break;
            case "-":
                oper = new OperationSub();
                break;
            case "*":
                oper = new OperationMul();
                break;
            case "/":
                oper = new OperationDiv();
                break;
            default:
                oper = new Operation();
                break;
        }
        return oper;
    }
}

 

策略模式中的Context類:
class Context
{
    CashSuper csuper;
    public Context(CashSuper cs)
    {
        this.csuper = cs;
    }
    public double GetResult(double money)
    {
        //調用具體策略類的收費方法
        return csuper.acceptCash(money);
    }
}

 


1.首先看一下接收的參數:簡單工廠類中的 CreateOperate 方法接收的是字符串,返回的是一個 Operation 對象;而 Context 類初始化時需要接收一個 CashSuper 對象。
2.簡單工廠類中是根據接收的條件創建一個相應的對象,而 Context 類接收的是一個對象,可以調用方法去執行此對象的方法。
總結簡單工廠模式和策略模式

1.從類型上說:簡單工廠模式屬於創建型模式,而策略模式屬於行為型模式。
2.接下來,看一個小例子:

 斧子有很多種,有一個工廠專門負責生產各種需求的斧子。

    工廠模式:
    1)根據你給出的目的來生產不同用途的斧子,例如要砍人,那么工廠生產砍人斧子,要伐木就生產伐木斧子。
    2)即根據你給出一些屬性來生產不同行為的一類對象返回給你。
    3)關注對象創建
    策略模式:
    1)用工廠生產的斧子來做對應的事情,例如用砍人的斧子來砍人,用伐木的斧子來伐木。
    2)即根據你給出對應的對象來執行對應的方法。
    3)關注行為的選擇

3.簡單工廠模式:根據客戶選擇的條件,來幫客戶創建一個對象。
 策略模式:客戶給它一個創建好的對象,它來幫客戶做相應的事。
兩種模式的優缺點

首先來看一下兩種模式的客戶端代碼:
//簡單工廠模式的客戶端:
Operation op;
//交給簡單工廠類創建對象
op = OperationFactory.CreateOperate("+");
op.StrNumberA = 10;
op.StrNumberB = 20;
//調用生成對象的方法
double result = op.GetResult();
Console.WriteLine(result);
 
//策略模式的客戶端:
double total = 0;
private void btnOk_Click(object sender, EventArgs e)
{
    CashContext cc = null;
    //客戶端自己創建對象
    switch(cbxType.SelectedItem.ToString())
    {
        case: "正常收費":
            cc = new CashContext(new CashNormal());
            break;
        case: "滿300返100":
            cc = new CashContext(new CashReturn());
            break;  
        case: "打8折":
            cc = new CashContext(new CashRebate());
            break;  
    }
    //計算具體策略收取的費用,交給context類執行相應的方法,客戶端只需要接收返回的值就可以
    double acceptMoney = cc.GetResult(Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text));
    //計算總費用
    total += acceptMoney;
    listBox1.Items.Add("單價:" + txtPrice.Text + " 數量:" + txtNum.Text + " " + comboBox1.SelectedItem.ToString() + "總計:" + acceptMoney);
    lblResult.Text = total.ToString();
}

 

通過比較客戶端的代碼發現:
  簡單工廠模式:將對象的選擇創建交給了簡單工廠類,客戶端只需要輸入相應的條件就可以,不用負責對象的創建,但是需要客戶端自己調用算法類的方法。但是一旦需要增加新的運算類,比如開根運算,就要去修改簡單工廠類。
  策略模式:對象的選擇創建仍需要自己來做,但是將調用方法的職責交給了Context類。一旦需要增加新的策略需要修改客戶端。

因此,簡單工廠模式的缺點就是當有新的需求增加時,需要頻繁的修改工廠類。只用策略模式,當有新的需求增加時需要修改的是客戶端,客戶端仍然承擔着創建對象的職責,並沒有減輕客戶端的壓力。而將這兩種模式結合起來使用,則需要修改 Context 類,總之不是完美的。


免責聲明!

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



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