Strategy模式,对一系列的算法加以封装,为全部算法定义一个抽象的算法接口,并经过继承该抽象算法接口对全部的算法加以封装和实现,具体的算法选择交由客户端决定(策略)。
Strategy模式主要用于平滑的处理算法的切换
在软件构建过程当中,某些对象可能用到的算法多种多样,常常改变,若是将这些算法都编码到对象中,将会使得对象变得异常复杂;并且有时候支持不使用的算法也是一个性能负担。
如何在运行时根据须要透明地更改对象的算法?将算法与对象自己解耦,从而避免上述问题?
enum TaxBase { CN_Tax, US_Tax, DE_Tax, }; class SalesOrder{ TaxBase tax; public: double CalculateTax(){ //... if (tax == CN_Tax){ //或者switch开关语句 //CN*********** } else if (tax == US_Tax){ //US*********** } else if (tax == DE_Tax){ //DE*********** }//.... } };
不要静态的去看一个软件结构的设计,而是要动态的去看,这是设计模式的一个重要观点。
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){ //更改 //... } //.... } };
对扩展开发,对更改封闭。类模块尽量用扩展的方式来支持将来的变化,而不是找到源代码,用修改源代码的方式来面对将来的变化
上面代码两处红色部分就违反了开放封闭原则,带来了一个很大的复用性负担,软件要从新更改,从新编译,从新测试,从新部署,须要的代价十分大。
因此尽量使用扩展方式来解决问题,如何使用扩展方式来解决,就要用到Strategy模式
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); //多态调用,具体依赖于工厂建立的对象 //... } };
咱们将原来结构化设计中的一个个算法,变为TaxStrategy类的一个子类
相对于上面结构化思想相比较:功能是同样的。可是要比较好处,在设计领域要比较好处须要放在时间轴中去看
//扩展 //********************************* class FRTax : public TaxStrategy{ public: virtual double Calculate(const Context& context){ //......... } };
其余地方不须要修改,固然工厂中须要去修改使得可以产生法国税务对象
多态调用会天然找到咱们扩展的税务算法运算
在总体代码中,尤为是SalesOrder主调用类中,代码不须要改变,获得了复用性
注意:咱们在新的文件中写入咱们扩展的算法,单独编译,例如dll,动态加入程序中,遵循了开闭原则
而第一种方法中在方法内部去修改添加源代码,打破了开放封闭原则,违背了复用性
注意:咱们在面向对象中(尤为是设计模式领域中)谈到的复用性是指编译单位,二进制层次上的复用性
例如咱们第二种方法,在编译部署后,不须要修改原来的文件,咱们只须要将新添加的扩展单独编译,动态被调用便可。原来二进制文件的复用性很好。
可是对于第一种方法,咱们对原方法进行了修改,咱们的整个程序都要从新编译,部署,原来的二进制文件再也不使用,而是须要咱们新的编译后的二进制文件,因此复用性极差
例以下面的修改:算法
if (tax == CN_Tax){ //CN*********** } else if (tax == US_Tax){ //US*********** } else if (tax == DE_Tax){ //DE*********** } else if (tax == FR_Tax){ //更改 //... }
并且在下面补充一段代码是颇有问题的,虽然咱们保证上面代码不变,可是在实际开发中,向后面添加代码,每每会打破这个代码前面的一些代码,会向前面引入一些bug。并且咱们对这种不叫作复用,真正的复用是编译层面的
定义一系列算法,把他们一个个封装起来,而且使他们能够互相替换(变化<各个算法>)。该模式使得算法可独立于使用它的客户程序(稳定<SalesOrder类>)而变化(扩展,子类化)
Strategy基类中通常放置的方法很少
除了极个别,像单例。其余的均可以像上面同样去找出它的稳定部分和变化部分
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); //在运行时就支持多态调用,灵活变化 //... } };
通常来讲代码出现if..else...或者switch...case...,这就是咱们须要Strategy模式的特征。(固然咱们说的是变化的,如果像男女性别判断,就是绝对不变的,就不须要使用到Strategy模式,可是更多场景都是可变的) 由于if..else..是结构化思惟中的分而治之。并且须要一直判断,支持不使用的算法也是一个性能负担。并且由很大一段代码都被装载在代码段中,这是不须要的。 咱们使用Strategy模式,是在运行时加载,运行中须要哪一个就能够即时调用。具备稳定性。 在运行时代码在代码段中,放在内存中,最好的是加载在高级缓存中,最快,如果代码段过长,就只能放在主存,甚至虚拟内存中(硬盘),因此咱们使用if...else..时,会有不少代码被加载到高级缓存,内存中,这会占用空间,其余代码就会被挤入其余地方,执行不会太快,Strategy模式会顺便解决部分这个问题
//策略基类 class Strategy { public: virtual void SymEncrypt() = 0; virtual ~Strategy(){}; };
//各个算法 class Des:public Strategy { public: virtual void SymEncrypt() { cout << "Des Encrypt" << endl; } }; class AES :public Strategy { public: virtual void SymEncrypt() { cout << "AES Encrypt" << endl; } };
//上下文管理器 class Context { private: Strategy* s; public: Context(Strategy* strategy) { s = strategy; //这里应该由工厂实现 } void Operator() { s->SymEncrypt(); } };
void main() { Strategy* strategy = NULL; Context* ctx = NULL; strategy = new AES; ctx = new Context(strategy); ctx->Operator(); delete strategy; delete ctx; system("pause"); return; }