策略模式
策略模式
GOF中定义
定义一系列算法,把它们一个个封装起来,并且使它们可互相替换(变化)。该模式使得算法可独立于使用它的客户程序(稳定)而变化(扩展,子类化)。
动机
-
在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都编码到对象中,将会使对象变得异常复杂;而且有时候支持不使用的算法也是一个性能负担。
-
如何在运行时根据需要透明的更改对象的算法?将算法与对象本身解耦,从而避免上述问题?
示例
计算税收,不同国家有不同税收的计算方法,需要分别对不同的计算方法使用不同的算法模型。
//枚举类型
enum TaxBase {
CN_Tax,
US_Tax,
DE_Tax
};
class SalesOrder{
TaxBase tax;
public:
double CalculateTax(){
//...
if (tax == CN_Tax){
//CN***********
}
else if (tax == US_Tax){
//US***********
}
else if (tax == DE_Tax){
//DE***********
}
//....
}
};
在本例中,将所有税收的计算方法都置于SalesOrder的CalculateTax()中,这样会带来两个矛盾:
- 若增加其他国家税收的计算方法,是在程序本身增加代码,违反开闭原则,且改动处较多。改动处:枚举类型内部添加FR_Tax和CalculateTax()中增加else if (tax == FR_Tax){...};
- 若新增的税收计算方法较多,则会使得CalculateTax()方法变得臃肿,并且很可能会包含使用频率很低的计算方法。
那么不再使用枚举类型来实现,看下面代码
class TaxStrategy{
public:
virtual double Calculate(const Context& context)=0;
virtual ~TaxStrategy(){}
};
class CNTax : public TaxStrategy{
public:
virtual double Calculate(const Context& context){
//***********
}
};
class USTax : public TaxStrategy{
public:
virtual double Calculate(const Context& context){
//***********
}
};
class DETax : public TaxStrategy{
public:
virtual double Calculate(const Context& context){
//***********
}
};
//扩展
//*********************************
class FRTax : public TaxStrategy{
public:
virtual double Calculate(const Context& context){
//.........
}
};
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); //多态调用
//...
}
};
在本代码中,
- 定义一个TaxStrategy类,此类为纯虚基类,类中定义纯虚函数Calculate()方法。
- 分别使用不同的子类继承基类,并重写不同国家的税收计算方法Calculate()方法
- SalesOrder类内部放一个TaxStrategy基类类型指针,便于使用多态机制根据指针类型去调用不同国家的税收计算方法,得到结果,使得代码具有良好的本地性。
优点:
-
由于提取处了抽象层TaxStrategy类,并在启动定义接口Calculate()方法。在后续扩展新的税收计算方法时,直接新扩展一个类并在该类中重写Calculate()即可,如程序中FRTax所示,不需修改原来代码,符合开闭原则,便于扩展。同时使用增量编译可以减少编译过程中的时间成本等。
-
并没有将所有的计算方法都包含在一个计算方法中,可适当提高计算性能。
做出上述程序的类图

在上述图中,Context和Strategy部分是稳定的,而Strategy类的子类重写了税收计算方法Calculate(),该方法会随着项目的需求发生变化,故本部分是变化的。
在应对Calculate()计算方法的变化时,可通过新增Strategy类的子类并重写Calculate()来实现。
要点总结
- Strategy及其子类为组件提供了一系列可重用的算法,从而可以使得类型在运行时方便地根据需要在各个算法之间进行切换。
- Strategy模式提供了用条件判断语句以外的另一种选择,消除条件判断语句,就是在解耦合。含有许多条件判断语句的代码通常都需要Strategy模式。
- 如果Strategy对象没有实例变量,那么各个上下文可以共享同一个Strategy对象,从而节省对象开销。
浙公网安备 33010602011771号