3.2 Strategy (策略模式)
动机:在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都编码到对象中,将会使对象变得异常复杂,而且有时候支持不使用或不常使用的算法也是一个性能负担。
如何在运行时根据需要透明地改变对象的算法?将算法和对象本身解耦,从而避免上述问题?
代码示例:
如果使用第一种方法,需要更改枚举类型和if else语句(第6行和24行),违背了开放封闭原则(对扩展开发,对修改封闭);
采用第二种方法(策略模式),定义税法基类,对不同国家税法定义不同子类,override Calculate方法。59行,多态调用。
假设出现变化:扩展32 - 39行,SalesOrder不变(复用性,遵循开放封闭原则)。
补:面向对象提到的复用性,指的是编译单位(二进制单位)的复用性,不是粘贴代码的复用性
1 // strategy1.cpp 2 enum TaxBase { 3 CN_Tax, 4 US_Tax, 5 DE_Tax, 6 FR_Tax //更改 7 }; 8 9 class SalesOrder{ 10 TaxBase tax; 11 public: 12 double CalculateTax(){ 13 //... 14 15 if (tax == CN_Tax){ 16 //CN*********** 17 } 18 else if (tax == US_Tax){ 19 //US*********** 20 } 21 else if (tax == DE_Tax){ 22 //DE*********** 23 } 24 else if (tax == FR_Tax){ //更改 25 //... 26 } 27 28 //.... 29 } 30
1 //Strategy.cpp 2 class TaxStrategy{ 3 public: 4 virtual double Calculate(const Context& context)=0; 5 virtual ~TaxStrategy(){} 6 }; 7 8 9 class CNTax : public TaxStrategy{ 10 public: 11 virtual double Calculate(const Context& context){ 12 //*********** 13 } 14 }; 15 16 class USTax : public TaxStrategy{ 17 public: 18 virtual double Calculate(const Context& context){ 19 //*********** 20 } 21 }; 22 23 class DETax : public TaxStrategy{ 24 public: 25 virtual double Calculate(const Context& context){ 26 //*********** 27 } 28 }; 29 30 31 32 //扩展 33 //********************************* 34 class FRTax : public TaxStrategy{ 35 public: 36 virtual double Calculate(const Context& context){ 37 //......... 38 } 39 }; 40 41 42 class SalesOrder{ 43 private: 44 TaxStrategy* strategy; 45 46 public: 47 SalesOrder(StrategyFactory* strategyFactory){ 48 this->strategy = strategyFactory->NewStrategy(); 49 } 50 ~SalesOrder(){ 51 delete this->strategy; 52 } 53 54 public double CalculateTax(){ 55 //... 56 Context context(); 57 58 double val = 59 strategy->Calculate(context); //多态调用 60 //... 61 } 62 63 };
定义: 定义一系列算法, 把它们一个个封装起来,并且使它们可以互相替换(变化)。该模式使得算法可独立于使用它的客户程序(稳定)而变化(扩展,子类化)。
类图:
要点总结:
Strategy及其子类为组件提供了一系列可重用的算法,从而可以得到类型在运行时方便地根据需要在各个算法之间进行切换。
Strtegy模式提供了用条件判断语句以外的另一种选择消除条件判断语句,就是在解耦合。含有许多条件判断语句的代码通常都需要Strategy模式。(除非if else语句绝对不变,如对一周七天判断等)
如果Strategy对象没有实例变量,那么各个上下午可以共享同一个Strategy对象,从而节省对象开销。