我们构建设计模式的时候,不管是什么模式都是从稳定与改变来考虑。本文所说的策略模式依旧从这两方面进行思考,结合如下计算税务的代码例子进行说明
/**
* 未使用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); //多态调用
//...
}
}
那么所谓的性能优化是什么呢?这个不是它的重点,但可以提一下,代码运行得最快肯定是将代码放到CPU高级缓存里,用 if -else 语句与用多态调用的语句来比,明显多态调用语句占用的缓存较小。缓存不足会引起一些问题,比如当缓存不足时就会将一些代码挤出去放到内存里。
(1)模式动机
在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都编码到对象中,将会使对象变得异常复杂,而且有时候支持不使用的算法也是一个性能负担。大部分情况下当看到 if - else 语句时就应该思考是否需采用策略模式(如果从时间维度来看,if - else是固定不变的,则不需采用策略模式)
如何在运行时根据需要透明地更改对象的算法,将算法与对象本身解耦,从而避免上述问题?
(2)模式介绍
Strategy模式定义一系列算法,把它们一个个封装起来,并且使它们可互相替换(变化)。该模式使得算法可独立于使用它的客户程序(稳定)而变化(扩展,子类化)
(3)要点总结
a). Strategy及其子类为组件提供了一系列可重用的算法,从而可以使得类型在运行时方便地根据需要在各个算法之间进行切换
b). Strategy模式提供了用条件判断语句以外的另一种选择,消除条件判断语句,就是在解耦合。含有许多条件判断语句的代码通常都需要Strategy模式
c). 如果Strategy对象没有实例变量,那么各个上下文可以共享同一个Strategy对象,从而节省对象开销
在第一点中已展示出相应的代码,这里不做过多的赘述。我们应该要去好好把握稳定与变化间的关系,这才是该Strategy策略模式的重要点。