【設計模式】商場促銷 -- 策略模式
一,概念
策略模式定義了一系列的演算法,並將每一個演算法封裝起來,而且使它們還可以相互替換。策略模式讓演算法獨立於使用它的客戶而獨立變化。
二,策略模式的組成
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就可以了
相對與策略模式的提升為:客戶端需要承擔的判斷更少了,更簡潔了