设计模式C++002__策略模式
1、动机:
在软件构建过程中,某些对象使用的算法是多种多样的,经常改变,如果将这些算法都编码到对象中,将会使对象变得异常复杂;而且有时候支持不使用的算法也是一个性能负担。
?如何在运行时根据需要透明地更改对象的算法?将算法与对象本身解耦,从而避免上述问题?
2、策略模式:
定义一系列算法,把他们一个个封装起来,并且使他们可互相替换(变化)。该模式使得算法可以独立于使用它的客户程序(稳定)而变化(扩展,子类)。--GoF
例如跨国电商公司,订单计算税费的功能。
前期只有3个国家的业务,就只定义3个算法,使用枚举类区分。如果要在新的国家扩展业务。改动代码出如下,枚举定义处要改,还要在订单类里添加新的计算方法。
违反了开闭原则。
enum TaxBase {
CN_Tax,
US_Tax,
DE_Tax,
FR_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***********
}
else if (tax == FR_Tax){ //更改
//...
}
//....
}
};
如果使用策略模式,定义一个税费计算抽象基类,基类中只有一个计算税费的虚方法,不同国家的税费计算方法继承自抽象基类,各自实现自己的税费计算方法。
如果要支持新的税费计算方法,只需新建一个子类,根据实际情况实现即可。调用时利用C++的多态性质,在主函数中父类的指针指向子类对象,再用指针调用计算税费方法。
理论上,只要是if-else, switch-case 中可能会扩展新的分支以支持业务。都可以改为策略模式。
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; //这里声明一个虚基类指针,访问类型为private
public:
SalesOrder(StrategyFactory* strategyFactory){
this->strategy = strategyFactory->New Strategy();//这里就是子类实例对象的产生的地方,可能是一个工厂方法产生的。
}
~SalesOrder(){
delete this->strategy;
}
public double CalculateTax(){
//...
Context context();
double val =
strategy->Calculate(context); //多态调用
//...
}
};
3、策略模式相比 if-else这种分支处理。可以在不改变原有类的基础上,只新增子类,就可以实现对原有功能的扩展。符合开闭原则。
实际上,这也是利用了晚绑定的原理,调用哪一种方法。
结构:
4、要点总结:
- Strategy及其子类为组提供了一些列可重用的算法,从而使得类型在运行时方便的根据需要再各个算法之间进行切换。
- Strategy模式同了用条件判断语句以外的一种选择,消除条件判断语句,就是在解耦合。含有许多条件判断语句的代码通常都需要Strategy模式。
- 如果Strategy对象没有实例变量,那么各个上下文可以共享同一个Strategy对象,从而节省对象开销。