1. 程式人生 > >【設計模式】商場促銷 -- 策略模式

【設計模式】商場促銷 -- 策略模式

一,概念

        策略模式定義了一系列的演算法,並將每一個演算法封裝起來,而且使它們還可以相互替換。策略模式讓演算法獨立於使用它的客戶而獨立變化。

二,策略模式的組成

   1)抽象策略角色: 策略類,通常由一個介面或者抽象類實現。    2)具體策略角色:包裝了相關的演算法和行為。

   3)環境角色:持有一個策略類的引用,最終給客戶端呼叫。

三,補充C++知識

類物件的構造順序是這樣的:
  1.分配記憶體,呼叫建構函式時,隱式/顯示的初始化各資料成員
  2.進入建構函式後在建構函式中執行一般計算
       1)類裡面的任何成員變數在定義時是不能初始化的。
       2)一般的資料成員可以在建構函式中初始化。
       3)const資料成員必須在建構函式的初始化列表中初始化。
       4)static要在類的定義外面初始化。  
       5)陣列成員是不能在初始化列表裡初始化的。
       6)不能給陣列指定明顯的初始化。 
這6條一起,說明了一個問題:C++裡面是不能定義常量陣列的!因為3和5的矛盾。這個事情似乎說不過去啊?沒有辦法,我只好轉而求助於靜態資料成員。
到此,我的問題解決。但是我還想趁機複習一下C++類的初始化:
  1.初始化列表:CSomeClass::CSomeClass() : x(0), y(1){}
  2.類外初始化:int CSomeClass::myVar=3;
  3.const常量定義必須初始化,C++類裡面使用初始化列表;
  4.C++類不能定義常量陣列。

在C++類中,必須做如下事情:

1.必須對任何const或引用型別成員以及沒有預設建構函式的 類 型別 的任何成員 顯示地使用初始化列表進行初始化

2.類成員在定義時是不能被初始化的

3.類的成員初始化順序與成員變數在建構函式中的位置選後順序無關,至於成員變數在類中定義的先後順序有關

C++預設繼承方式為private

C++ new 生成的物件為指標,所以new 前面的物件要宣告為指標型別

四,例項

        計算器簡單工廠模式的精簡實現

#include <iostream>
using namespace std;

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;
	
    //在C#中可以用反射來取消判斷時用的switch,在C++中用什麼呢?RTTI??
	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;
}

將簡單工廠模式優化為策略模式後的程式碼如下:
#include <iostream>
using namespace std; 

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

//策略具體類-加法類
class AddOperation : public COperation
{
public:
	AddOperation(int a,int b)
	{
		m_nFirst=a;
		m_nSecond=b;
	}
	virtual double GetResult()
	{
		return m_nFirst+m_nSecond;
	}
};

class Context//策略類 
{
private:
	COperation* op;
public:
    Context(COperation* temp)//引數為策略基類(傳遞的時候被初始化為子類) 
	{
        op=temp;
	}
	double GetResult()
	{
		return op->GetResult();
	}
};

//客戶端
int main()
{
	int a,b;
    char c;
	cin>>a>>b;
    cout<<"請輸入運算子:";
    cin>>c;
    switch(c)
    {
       case '+':
	        Context *context=new Context(new AddOperation(a,b));
     	        cout<<context->GetResult()<<endl;
            break;
       default:
            break;
    }
	return 0;
}

這裡將策略(操作符)封裝成一個Context類,通過傳遞操作符子物件來返回相應子物件下操作結果。

菜鳥實現工廠模式和策略模式

        客戶端只需訪問Context類,而不用知道其它任何類資訊,實現了低耦合。在上例基礎上,修改下面內容

#include <iostream>
using namespace std;
 
 
 //策略基類
class COperation
{
public:
	int m_nFirst;
	int m_nSecond;
	virtual double GetResult()
	{
		double dResult=0;
		return dResult;
	}
};

//策略具體類-加法類
class AddOperation : public COperation
{
public:
	AddOperation(int a,int b)
	{
		m_nFirst=a;
		m_nSecond=b;
	}
	AddOperation()
	{
		m_nFirst=0;
		m_nSecond=0;
	}
	virtual double GetResult()
	{
		return m_nFirst+m_nSecond;
	}
};
class Context
{
private:
	COperation* op;
public:
    Context(char cType)
	{
        switch (cType)
        {
		case '+':
			op=new AddOperation(3,8);
			break;
		default:
			op=new AddOperation();
			break;
        }
	}
	double GetResult()
	{
		return op->GetResult();
	}
};
//客戶端
int main()
{
	Context *test=new Context('+');
	cout<<test->GetResult()<<endl;
	return 0;
}

五,商場促銷例子

        這裡想說的是,abstract 關鍵字是微軟為C#定義的,抽象類關鍵字。C++中沒有抽象類,如果想成為抽象類在類中定義純虛擬函式就可以了。只要擁有純虛擬函式的類就是抽象類,由於抽象類包含了沒有定義的純虛擬函式,所以不能定義抽象類的物件。

         商場促銷的簡單工廠實現:

#include <iostream>

using namespace std;

class CashSuper //現金收費抽象類
{
public:
    virtual double acceptCash(double money)//不能設定為純虛擬函式,後面還要生成物件用呢!!!! (不能只定義,而不實現)
    {
        
    }
};

class CashNormal : public CashSuper //收費子類,正常收費
{
public:
     double acceptCash(double money)
     {
         return money;
     }
    
};

class CashRebate : public CashSuper //打折收費子類
{
private:
    double moneyRebate;//不允許初始化
public:
    CashRebate(double moneyRebate)
    {
        this->moneyRebate=moneyRebate;
    }
    double acceptCash(double money)
    {
         return money*moneyRebate;
    }
};

class CashReturn :public CashSuper //返利收費子類(預設繼承為私有繼承)
{
private:
    double moneyCondition ;//返利購買額度
    double moneyReturn ;//返利多少
public:
    CashReturn(double moneyCondition,double moneyReturn)
    {
        this->moneyCondition=moneyCondition;
        this->moneyReturn=moneyReturn;
    }
    double acceptCash(double money)
    {
        double result = money;
        if(money >= moneyCondition )
            result = money-(money/moneyCondition)*moneyReturn;
        return result;
        
    }
};

class CashFactory
{
public:
    static CashSuper* creatCashAccept(string type)//根據子類type來生成相應收費子類
    {
        CashSuper *cs;
        if(type=="CashNormal")
            cs=new CashNormal();
        else if(type == "CashRebate")
            cs=new CashRebate(0.8);
        else if(type == "CashReturn")
            cs=new CashReturn(300,100);
        
        return cs;
    }
};

int main(int argc, char** argv) {

    CashSuper *csuper = CashFactory::creatCashAccept("CashRebate");
    double result=csuper->acceptCash(500);//500打八折,應該輸出 400
    cout<<"CashRebate 500 is:"<<result<<endl;
    return 0;
}

    策略模式:定義了演算法家族,分別封裝起來,讓它們之間可以相互替換,演算法的變化不會影響使用演算法的客戶。
        主要升級就是,將演算法封裝到Context中,然後通過傳遞物件生成相應子類物件,然後得到結果。
#include <iostream>

using namespace std;

class CashSuper //現金收費抽象類
{
public:
    virtual double acceptCash(double money)//不能設定為純虛擬函式,後面還要生成物件用呢!!!! 
    {
        
    }
};

class CashNormal : public CashSuper //收費子類,正常收費
{
public:
     double acceptCash(double money)
     {
         return money;
     }
    
};

class CashRebate : public CashSuper //打折收費子類
{
private:
    double moneyRebate;//不允許初始化
public:
    CashRebate(double moneyRebate)
    {
        this->moneyRebate=moneyRebate;
    }
    double acceptCash(double money)
    {
         return money*moneyRebate;
    }
};

class CashReturn :public CashSuper //返利收費子類(預設繼承為私有繼承)
{
private:
    double moneyCondition ;//返利購買額度
    double moneyReturn ;//返利多少
public:
    CashReturn(double moneyCondition,double moneyReturn)
    {
        this->moneyCondition=moneyCondition;
        this->moneyReturn=moneyReturn;
    }
    double acceptCash(double money)
    {
        double result = money;
        if(money >= moneyCondition )
            result = money-(money/moneyCondition)*moneyReturn;
        return result;
        
    }
};

/*class CashFactory
{
public:
    static CashSuper* creatCashAccept(string type)//根據子類type來生成相應收費子類
    {
        CashSuper *cs;
        if(type=="CashNormal")
            cs=new CashNormal();
        else if(type == "CashRebate")
            cs=new CashRebate(0.8);
        else if(type == "CashReturn")
            cs=new CashReturn(300,100);
        
        return cs;
    }
};*/

class CashContext
{
private:
    CashSuper cs;
public:
    CashContext(CashSuper csuper)
    {
        this->cs=csuper;
    }
    double GetResult(double money)
    {
        return cs.acceptCash(money);
    }
};

int main(int argc, char** argv) {

    //CashSuper *csuper = CashFactory::creatCashAccept("CashRebate");
    //double result=csuper->acceptCash(500);//500打八折,應該輸出 400
    
    
     CashContext *cs;
     string type="CashRebate";
     
     if(type=="CashNormal")
            cs=CashContext(new CashNormal());
     else if(type == "CashRebate")
            cs=CashContext(new CashRebate(0.8));
     else if(type == "CashReturn")
            cs=CashContext(new CashReturn(300,100));
        
        
    cout<<"CashRebate 500 is"<<cs->GetResult(500)<<endl;
    return 0;
}


這樣仍然存在缺點:就是讓客戶端來判斷生成哪個子類。

改進策略是,讓策略模式和工廠模式結合

#include <iostream>

using namespace std;

class CashSuper //現金收費抽象類
{
public:
    virtual double acceptCash(double money)//不能設定為純虛擬函式,後面還要生成物件用呢!!!! 
    {
        
    }
};

class CashNormal : public CashSuper //收費子類,正常收費
{
public:
     double acceptCash(double money)
     {
         return money;
     }
    
};

class CashRebate : public CashSuper //打折收費子類
{
private:
    double moneyRebate;//不允許初始化
public:
    CashRebate(double moneyRebate)
    {
        this->moneyRebate=moneyRebate;
    }
    double acceptCash(double money)
    {
         return money*moneyRebate;
    }
};

class CashReturn :public CashSuper //返利收費子類(預設繼承為私有繼承)
{
private:
    double moneyCondition ;//返利購買額度
    double moneyReturn ;//返利多少
public:
    CashReturn(double moneyCondition,double moneyReturn)
    {
        this->moneyCondition=moneyCondition;
        this->moneyReturn=moneyReturn;
    }
    double acceptCash(double money)
    {
        double result = money;
        if(money >= moneyCondition )
            result = money-(money/moneyCondition)*moneyReturn;
        return result;
        
    }
};

/*class CashFactory
{
public:
    static CashSuper* creatCashAccept(string type)//根據子類type來生成相應收費子類
    {
        CashSuper *cs;
        if(type=="CashNormal")
            cs=new CashNormal();
        else if(type == "CashRebate")
            cs=new CashRebate(0.8);
        else if(type == "CashReturn")
            cs=new CashReturn(300,100);
        
        return cs;
    }
};*/

class CashContext
{
private:
    CashSuper cs;
public:
    CashContext(string type)
    {
        if(type=="CashNormal")
            this->cs=new CashNormal();
        else if(type == "CashRebate")
            this->cs=new CashRebate(0.8);
        else if(type == "CashReturn")
            this->cs=new CashReturn(300,100);
              
    }
    double GetResult(double money)
    {
        return cs.acceptCash(money);
    }
};

int main(int argc, char** argv) {

     CashContext *cs=new CashContext("CashRebate");
           
     cout<<"CashRebate 500 is"<<cs->GetResult(500)<<endl;
    return 0;
}


這樣客戶端只需要更改金額和打折手段就可以了。相對簡單工廠的提升為:簡單工廠需要讓客戶呼叫兩個類SuperCash和CashFactory。而結合之後僅僅需要呼叫CashContext就可以了

相對與策略模式的提升為:客戶端需要承擔的判斷更少了,更簡潔了