Strategy 模式和 Template 模式要解决的问题是相同(类似)的,都是为了给业务逻辑(算法)具体实现和抽象接口之间的解耦。Strategy 模式将逻辑(算法)封装到一个类(Context)里面,通过组合的方式将具体算法的实现在组合对象中实现,再通过委托的方式将抽象接口的实现委托给组合对象实现。
问题提出
假设要实现一个税法计算程序,能够计算出不同国家的税率(OK,别去管怎么算的问题,总之,返回一个 double 型的结果就完事了)。
解法1.0
第一种方法就是通过一个枚举类型的变量来标识计算税率的国家,通过判断这个变量的值来确定具体计算哪一个国家的税率。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| enum TaxBase { CN_Tax, US_Tax, DE_Tax, FR_Tax };
class SalesOrder { TaxBase tax; public: SalesOrder(TaxBase t) : tax(t) {
} double CalculateTax() { switch (tax) { case CN_Tax: { } break; case US_Tax: { } break; case DE_Tax: { } break; case FR_Tax: { } break; } }
};
int main() { SalesOrder order(TaxBase::CN_Tax); order.CalculateTax(); return 0; }
|
这种代码模式下,如果需要新增一个国家的计算功能,需要修改两处代码,并且要进行重新编译,而且很多时候,这样添加代码并不像想象中那么简单,新增的代码往往会对之前的代码造成不良影响。
解法2.0
实现一个税法计算策略基类 TaxStrategy ,其中提供一个纯虚函数 Calculate ,通过继承此类并实现这一接口就可以实现一个新的国家的计算方法。在工程环境下,一个类往往要放在一个独立的文件中,这意味着原先的代码将不用重新参与编译,极大降低了代码扩展的难度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| 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() { if (nullptr != strategy) delete this->strategy; } double CalculateTax() { Context context(); double val = strategy->Calculate(context); } }
|
总结
下面是 策略模式(Strategy) 的类图
