C++设计模式之策略模式
_学习B站李建忠老师课程记录
动机:
1.在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都编码到对象中,将会使得对象变得异常复杂;而且有时候支持不使用的算法也是一种性能负担。
2.如何在运行时根据需要透明地更改对象的算法? 将本算法与对象本身解耦合,从而避免上述问题?
模式定义:
定义一系列算法,把它们一个个封装起来,并且使它们可以互相替换(变化),该模式使得算法可独立于使用它的客户程序(稳定)而变化(扩展,子类化)。-GoF
重构前代码 税率计算举例:
#include <iostream>
#include<vector>
using namespace std;
enum TaxBase
{
CN_Tax = 0,
US_Tax = 1,
DE_Tax = 2,
FR_Tax = 3
};
class SaleOrder {
private:
TaxBase m_tax;
public:
SaleOrder(TaxBase tax):m_tax(tax){ }
~SaleOrder(){}
public:
double CalculateTax() {
if (m_tax == CN_Tax) {
//CN.........
}
else if (m_tax == US_Tax) {
//US......
}
else if (m_tax == DE_Tax) {
//DE...........
}
else if (m_tax == FR_Tax) {
//FE.........
}
return 0;
}
};
int main() {
SaleOrder myorder(TaxBase::FR_Tax);
double tax=myorder.CalculateTax();
cout << tax << endl;
return 0;
}
问题思考:
如果加新西兰等等税法,需要更改的函数,添加诸多的if else语句 。
double CalculateTax() {}
重构后代码:
#include <iostream>
using namespace std;
class TaxStrategy {//税率的策略基类 为纯虚基类 抽象类
public:
virtual double Calculate(const Context& context) = 0;
virtual ~TaxStrategy(){}//基类一定要把基类的析构函数声明为virtual
};
// 添加扩展1:中国税率
class CN_Tax :public TaxStrategy {
public:
virtual double Calculate(const Context& context) {
// Override:实现China税率的计算方法
return 0;
}
};
// 添加扩展2:美国税率
class US_Tax :public TaxStrategy {
public:
virtual double Calculate(const Context& context) {
// Override:实现US_Tax税率的计算方法
return 0;
}
};
// 添加扩展2:法国税率
class FR_Tax :public TaxStrategy {
public:
virtual double Calculate(const Context& context) {
// Override:实现法国税率的计算方法
return 0;
}
};
//可以添加任意国家的税率计算方法 ,SaleOrder方法不变
class SalesOrder {
private:
TaxStrategy* strategy;//多态指针 不能设置为对象,失去多态性,&引用很少用 父类指针可以指向不同的子类
public:
SalesOrder(StrategyFactory* strategyFactory) {//此处采用简单工厂模式 返回一个堆对象 是一个确定的国家对象 需要自己释放
this->strategy = strategyFactory->NewStrategy();
}
~SalesOrder() { delete this->strategy; }
public :
double CalculateTax() {
//...
Context context;
double val =strategy->Calculate(context); //动态联编 多态调用 什么国家调用什么税法计算方法
return val;
//...
}
};
int main() {
StrategyFactory* strategy_factory;
SalesOrder saleorder(strategy_factory);//调用构造函数传参
double tax=saleorder.CalculateTax();
cout << tax << endl;
return 0;
}
要点总结
1.如果出现if else的分类绝对不会变,可以不采用策略模式;如果从时间轴发展的角度,一个程序代码可能在未来扩展。
2.Strategy及子类为组件提供了一些列可重用的算法,从而使得类型在运行时方便根据需要在各算法之间切换。
3.策略模式提供了用条件判断语句以外的一种可扩展的选择,消除条件判断语句,就是在解耦合,含有许多条件判断语句代码通常都需要策略模式;
4.如果策略对象没有实例变量,那么各个上下文可以共享一个策略对象,从而节省对象开销.
tips:运行时候最好加载到CPU的高级缓存里,如果代码段过长,需要放到主存里,甚至放在虚拟内存/硬盘里,一些代码不真正使用却装载到CPU高级缓存里,其他代码可能不能放在高级缓存里,影响运行效率。