前言
在学习侯捷老师的有关设计模式的课程(李建忠老师主讲)中,老师对23种设计模式的有自己的划分,如下。所以老师讲解是按照这种顺序讲解。
组件协作:
Strategy
1.使用场合
简单说就是,就是可能今天调用"加法";明天可能调用"乘法";后天发现又需要"除法"了,所以代码需要加一个“除法”的算法。
这些算法是由客户程序
决定使用哪一种。
这其实就像多重if else的条件选择语句。
2.案例1
我们可以想想下面一种场景:不同国家有不同的税率,我们需要根据是哪个国家,调用不同的税率计算方式。
方式1:
#include <iostream>
using namespace std;
enum TaxBase{
CN_Tax,
US_Tax,
DE_Tax,
};
class SalesOrder{
TaxBase tax;
public:
SalesOrder(){}
SalesOrder(TaxBase t):tax(t){}
double CalculateTax(){
//...
if (tax == CN_Tax){
cout << "CN tax law" << endl;
return 1.1;
}
else if (tax == US_Tax){
cout << "US tax law" << endl;
return 1.2;
}
else if (tax == DE_Tax){
cout << "DE tax law" << endl;
return 1.3;
}
}
};
int main()
{
SalesOrder s(CN_Tax);
s.CalculateTax();
system("pause");
return 0;
}
这种情况下,虽然程序开发者封装好了这样的算法,只需要生成一个SalesOrder对象,然后传入是哪个国家的税法就行。
但是这里有一个问题:一旦公司的业务有扩展,比如现在对接的国家需要加入法国,那么程序开发者势必要
- 在枚举
TaxBase
中加入FR_Tax
- 同时修改类
SalesOrder
的方法CalculateTax()
,加入一个计算FR_Tax
情况下的分支。
这样的话,明显违背了设计原则中的开闭原则
(对扩展开放,对更改封闭
),不应采取。下面看方法2,使用策略模式就能解决这个问题。
方法2:策略模式
#include <iostream>
using namespace std;
class TaxStrategy{
public:
virtual double Calculate() = 0;
virtual ~TaxStrategy(){}
};
class CNTax : public TaxStrategy{
public:
virtual double Calculate(){
cout << "CN tax law" << endl;
return 1.1;
}
};
class USTax : public TaxStrategy{
public:
virtual double Calculate(){
cout << "US tax law" << endl;
return 1.2;
}
};
class DETax : public TaxStrategy{
public:
virtual double Calculate(){
cout << "DE tax law" << endl;
return 1.3;
}
};
class SalesOrder{
private:
TaxStrategy* strategy;
public:
SalesOrder(TaxStrategy* TaxStrategy){
strategy = TaxStrategy;
}
~SalesOrder(){
delete this->strategy;
}
double CalculateTax(){
return strategy->Calculate(); //多态调用
}
};
int main()
{
SalesOrder s(new CNTax);
s.CalculateTax();
system("pause");
return 0;
}
当我们需要加入法国相关的祱率计算时,只需扩展
即可,不用修改已有的代码,如下:
class FRTax : public TaxStrategy{
public:
virtual double Calculate(){
cout << "FR tax law" << endl;
return 1.4;
}
};
3.案例2:
商场里有两种促销方式:上午满200减50;下午一律打8折。输入价格,根据是上午还是下午,打印出最终的成交价格。
#include<iostream>
using namespace std;
class PromoteSaleStrategy
{
public:
PromoteSaleStrategy(){}
virtual ~PromoteSaleStrategy(){}
virtual double CalculateMethod(double originalprice) = 0;//虚方法
};
class BackCashStrategy : public PromoteSaleStrategy
{
public:
virtual double CalculateMethod(double originalprice){//虚函数,多态
if (originalprice > 200){
std::cout << "返现50" << std::endl;
return originalprice - 50;
}
else{
std::cout << "无法返现" << std::endl;
return originalprice;
}
}
};
class DiscountStrategy : public PromoteSaleStrategy
{
public:
virtual double CalculateMethod(double originalprice){//虚函数,多态
std::cout << "打了8折" << std::endl;
return originalprice*0.8;
}
};
class SeLLProduct
{
public:
SeLLProduct(double price) :_price(price){}
~SeLLProduct(){}
void setStrategy(PromoteSaleStrategy* pss){
_pss = pss;
}
void Sell(){
//多态调用
std::cout << "当前价格:" << _pss->CalculateMethod(_price) << std::endl;
}
private:
PromoteSaleStrategy* _pss;
double _price;
};
int main()
{
double price;
cout << "请输入价格:" ;
cin >> price;
SeLLProduct item(price);
int n;
cout << "现在是上午还是下午?1:上午 2:下午" << endl;
cin >> n;
if (n == 1){
cout << "上午返现" << endl;
item.setStrategy(new BackCashStrategy);
item.Sell();
}
else if (n == 2){
cout << "下午打折" << endl;
item.setStrategy(new DiscountStrategy);
item.Sell();
}
system("pause");
return 0;
}
上述使用策略设计模式,可以很好应对以后有新增的促销方式,并且将促销策略和类SeLLProduct
解耦合。
4.模式定义
- 这里的"相互替换(变化)"就像案例1中的
CNTax USTax DETax
子类和案例2中的DiscountStrategy BackCashStrategy
子类。 - 这里"客户程序(稳定)"就像案例1中的
SalesOrder
和案例2中的SeLLProduct
,他们只需拿着基类指针即可。
5.结构
6.要点总结
- Strategy类及其子类提供了一系列可重用的算法,从而可以使得在运行时(指的是多态调用时)方便地根据需要选择算法。
- Strategy模式
提供了用条件判断语句以外的另一种选择
,消除条件判断语句,就是在解耦合。含有许多条件判断语句的代码通常都需要Strategy模式
。