策略模式
动机
在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都编码到对象中,将会使对象变得异常复杂; 而且有时候支持不使用的算法也是一个性能负担。
如何在运行时根据需要透明地更改对象的算法?将算法与对象本身解耦,从而避免上述问题?
解决方法
定义一系列算法,把它们一个个封装起来,并且使它们可互相替换(变化)。该模式使得算法可独立于使用它的客户程序(稳定)而变化(扩展,子类化)。
例子
计算财务报表(支持跨国税率):
一种实现:
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***********
}
// ....
}
};
当添加了一个税率需要改变地方:
-
enum添加税率算法
enum TaxBase { CN_Tax, US_Tax, DE_Tax, FR_Tax // 更改 };
-
修改对应税率计算
... else if (tax == DE_Tax) { // DE*********** } else if (tax == FR_Tax) // 更改 { // ... } ...
另一种实现:
// 税法的基类
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 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); //多态调用
// ...
}
};
当添加了一个税率需要改变地方(其实就是一个扩展,原来的代码无需改变):
class FRTax: public TaxStrategy
{
public:
virtual double Calculate(const Context& context)
{
// .........
}
};
优缺点
优点:算法可以自由切换。避免使用多重条件判断。扩展性良好。继承可以析取算法的公共部分。可以为每个算法单独定义测试类。
缺点:策略类会增多。所有策略类都需要对外暴露。