策略模式(Strategy)
一、策略模式(Strategy)
1. 模式动机
在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都编码到对象中,将会使对象变得异常复杂,而且有时候支持不使用的算法也是一个性能负担。代码如果出现很多个if else或者switch case且在后续可能会继续增加判断分支,就可以察看是否能使用策略模式。而在业务场景稳定不变的情况下则不需要使用策略模式。
如何在运行时根据需要透明地更改对象的算法,将算法与对象本身解耦,从而避免上述问题?运行时通过传递多态的变量,实现多态调用。
2. 模式定义
策略模式定义一系列算法,把它们一个个封装起来,并且使它们可互相替换(变化)。该模式使得算法可独立于使用它的客户程序(稳定)而变化(扩展,子类化)。
3. 要点总结
(1)策略模式及其子类为组件提供了一系列可重用的算法,从而可以使得类型在运行时方便地根据需要在各个算法之间进行切换。
(2)模式提供了用条件判断语句以外的另一种选择,消除条件判断语句,就是在解耦合。含有许多条件判断语句的代码通常都需要策略模式。
(3)如果Strategy对象没有实例变量,那么各个上下文可以共享同一个Strategy对象,从而节省对象开销。
三、实现
类图

代码
- 注意基类析构函数需要写虚函数,否则子类析构会有问题。
- 多态变量一般使用指针,也可以使用引用,但是会有其它问题。
- 有时候添加不一定使用的算法会降低运行效率(如代码段有很多个if else或者switch case,但运行时不一定会使用到),代码段最好加载到CPU高级缓存中以提高运行效率,太多个if else或者switch case可能会导致部分代码段被挤出到内存或硬盘中的虚拟内存中去进而导致运行效率下降。
- 优化性能不是使用策略模式的主要原因。
结构化软件设计实现代码
/**
* 未使用Strategy模式
* 缺点:一有变化就需要在主流程进行改动,且绝大部分情况下都会影响上面流程的代码
*/
enum TaxBase {
CN_Tax,
US_Tax,
DE_Tax,
FR_Tax //新增点
};
class SalesOrder{
TaxBase tax;
public:
double CalcTax(){
//... 流程1
if(tax == CN_Tax){
// CN*******
}
else if(tax == US_Tax){
// US*******
}
else if(tax == DE_Tax){
// DE*******
}
else if(tax == FR_Tax){
// 新增FR(法国)的税务计算方式,此时新增后绝大部分情况下也会影响上面 流程1 的代码
}
}
};
面向对象软件设计实现代码
/**
* 采取Strategy模式
* 优点:符合开放封闭原则(OCP),不会影响其上面的流程,当然也存在一定的性能优化
*/
class TaxStrategy{
public:
virtual double CalcTax(const Context& context) = 0;
virtual ~TaxStrategy(){}
};
class CNTax : public TaxStrategy{
public:
virtual double CalcTax(const Context& context){
//*************
}
};
class USTax : public TaxStrategy{
public:
virtual double CalcTax(const Context& context){
//*************
}
};
//...
class FRTax : public TaxStrategy{ //新增点,只需要继承并实现相应的方法就行。这就符合开放封闭原则,只需扩展下就行了
public:
virtual double CalcTax(const Context& context){
//*************
}
};
class SalesOrder{
private:
TaxStrategy* strategy; //这是多态指针,不能放一个对象(因为这是抽象类),所谓多态就是要该指针可以指向 TaxStrategy 不同的子类
public:
SalesOrder(StrategyFactory* strategyFactory){
this->strategy = strategyFactory->NewStrategy();
}
~SalesOrder(){
delete this->strategy;
}
double CalcTax(){
//...
Context context();
double val = strategy->CalcTax(context); //多态调用
//...
}
}