在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都包装在对象中,可能会使对象变得异常复杂;而且有时候支持不使用的算法也是一个性能负担。
- 模式定义:
定义一系列算法,把他们一个个封装起来,并且使他们可互相替换。该模式使得算法可独立于使用它的客户程序(稳定)而变化(扩展,子类化)。 - 下面以一个例子说明:
各个国家的税收标准都不一样,我们定义了一个枚举类型,在CalculateTax()
中根据传进来对象的tax属性选择if else
语句:
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){//添加
//...
}
//....
}
};
当新增一个国家的时候我们必须对类的CalculateTax重写,添加对应的新增的处理部分,这就违背了设计模式中的开放扩展,封闭修改的原则。
- 改进版本
首先定义一个计算税收的基类,声明Calculate
纯虚函数
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;
}
double CalculateTax(){
double val = strategy->Calculate(context); //多态调用
}
};
该类有一个基类指针的数据成员strategy
,构造函数中给该指针赋值,利用工厂方法将该基类指针指向对应的New出来的子类的对象。关于工厂方法后面会讲,这里暂且可以将构造函数改为:
SalesOrder(TaxStratgey* stra){
this->strategy = stra;
}
当传进来一个具体的类对象(CNTax,USTax等)的地址后,strategy
将指向该子类对象,在调用CalculateTax()
时,就会多态调用具体类的Calculate()
方法。
- 总结
策略模式及其子类为组件提供了一系列可重用的算法,从而可以使得类型在运行时方便的根据需要在各个算法之间进行切换。
. 策略模式提供了用条件判断语句以外的另一种选择,消除条件判断语句就是在解耦合,在含有这种代码的情况下,使用策略模式通常都是很好的选择;
. 如果Strategy对象没有实例变量,那么各个上下文可以共享同一个Strategy对象。