策略模式封装了变化
面向对象编程——并非类越多越好,不至于每一个功能的实现都写一个具体的类,而是抽象出他们之间的共性,类的划分是为了封装,但是分类的基础是抽象,具有相同属性和功能的对象的抽象集合才是类。
策略模式解析:
策略模式是一种定义一系列算法的方法,从概念上面来看,所有的算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少各种算法类与使用算法类之间的耦合。
策略模式优点:
Strategy类层次为Context定义了一系列的可供重用的算法和行为。继承有助于析取出这些算法中的公用功能。(例如CashContext中的GetResult来自于CashSuper)
策略模式的优点就是简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口独立测试。 (哪个算法出了问题,只需要修改对应的算法即可。)
当不同的行为堆砌在一个类中时,就很难避免使用条件语句来选择合适的行为。将这些行为封装在一个个独立的Strategy类中,可以在使用这些行为的类中消除条件语句。
策略模式避免了过多条件判断(if-else)语句的使用
策略模式是怎么避免过多的条件判断的?
策略模式就是用来封装算法的,但在实践当中,我们发现它可以用它来封装几乎任何类型的规则,只要在分析过程中知道需要在不同时间(不同应用场合)应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性。
基本的策略模式中,选择所用具体实现的职责由客户端对象承担,并转给策略模式的Context上下文对象。
在结合了工厂和策略模式后,选择所用具体实现的职责也可以由Context承担,最大化减轻了客户端的职责。
要增加一种算法的话,还是需要增加Context对象中的switch分支
但是——无论如何,任何需求的变更都是需要成本的。
只是成本开销大小的关系,面对同样的需求当然成本开销越小越好。
还提到一种反射技术——策略模式的改进
封装变化点的思想——Context上下文对象,通过使不同算法实现类继承同一个父类,创建该父类的一个引用来装载子类对象(父类可以装子类——就比如说Object对象可以装载所有的对象),通过这个父类的引用去调用不同子类中的算法实现。
主要的问题:
怎么装载子类?
——通过子类继承同一个父对象,创建一个父对象的引用来装载。
怎么装载不同的子类?
——通过构造方法传入不同策略对象来构造对不同子类的引用。
怎么调用子类的方法?
——子类通过继承override重写父类中的方法,然后通过父类的引用(这时候装载了子类)直接调用该方法即可。
实例:商场打折促销、满减促销等不同营销策略的实现——结合CashFactory——收费工厂类
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
| 商场打折促销、满减促销等不同营销策略的实现——
CashSuper——父类 CashNormal——算法1对应一个策略:常规收费 CashRebate——算法2对应一个策略:打折收费 CashReturn——算法3对应一个策略:满返收费 CashContext——收费上下文 可以在此继续拓展其他的策略类
public class CashContext{ private CashSuper cs; public CashContext(CashSuper csuper) { this.cs = csuper; } public double GetResult(double money) { return cs.AcceptCash(money); } }
结合CashFactory——收费工厂类
public static CashSuper createCashAccept(string type) { CashSuper cs = null; switch (type) { case "常规收费":{ cs = new CashNormal( ); break;} case "打折收费":{ cs1 = new CashRebate( 0.8);break; } case…… } return cs; }
客户端代码(main函数) CashContext cc=null; switch(int num) { case 0:{ cc = new CashContext( new CashNormal ( ) ) ; break; } case 1:{ cc = new CashContext( new CashRebate( 0.8 ) ) ; break; } … } double total=0f; total += cc.GetResult(); …
可以发现——客户端中又多了这个switch判断的过程,将它转移是可以做到的 ——简单工厂不一定要是单独的一个类,它可以和策略模式的Context上下文对象结合。
|
结合简单工厂和策略模式——修改后的CashContext
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
| 结合简单工厂和策略模式——修改后的CashContext
class CashContext { CashContext cc=null; public CashContext(string type) { switch(type) { case:"常规收费":{ cc = new CashContext( new CashNormal ( ) ) ; break; } case:"打折收费":{ cc = new CashContext( new CashRebate( 0.8 ) ) ; break; } … } } public double GetResult(double money) { return cs.acceptCash(money); } }
|
看看两种方式调用的区别:
//用工厂方式
CashSuper csuper = CashFactory.createFactory(string type);
//工厂和策略模式的结合
CashContext cashContext = new CashContext(string type);
结合工厂模式与策略模式的好处:
用工厂方式需要了解两个类对象CashSuper和CashFactory
用工厂和策略模式结合可以只了解上下文对象CashContext
这使得具体的收费算法彻底地与客户端分离,连算法的父类CashSuper都不对客户端开放。
策略模式的思想:
- 定义算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化不会影响使用该算法的客户。
- 算法本身只是一种策略,最重要的是这些算法是随时可能互相替换的,这就是变化点,而封装变化点是我们面向对象的一种很重要的思维方式。
策略模式标准实现
抽象算法类,Strategy
策略A继承自抽象算法(Strategy),ConcreteStrategyA
策略B继承自抽象算法(Strategy),ConcreteStrategyB
……
上下文对象,Context,用一个ConcreteStrategy来配置,维护一个对Strategy对象的引用。
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
| 抽象算法类 abstract class Strategy { void abstract void AlgorithmInterface(); }
class ConcreteStrategyA : Strategy { public override void AlgorithmInterface() { } }
class ConcreteStrategyB : Strategy { public override void AlgorithmInterface() { } }
class Context { Strategy strategy; public Context(Strategy strategy) { this.strategy = strategy; } public void ContextInterface() { strategy.AlgorithmInterface(); } }
客户端代码(main函数): Context context;
context = new Context(new ConcreteStrategyA( ));
context.ContextInterface( );
context = new Context(new ConcreteStrategyB( ));
context.ContextInterface( );
|
客户端是如何调用不同的算法的:
实例化了上下文对象,传入了继承Strategy类的ConcreteStrategyA类实例化的对象来初始化Context对象中的Strategy参数,此时使用的便是具体实现策略A的算法。调用Context对象的方法,即是调用策略A的方法实现,策略模式就是通过这样的上下文对象封装了“变化点”,每次采取不同的策略,就传入不同的继承自策略类Strategy的具体策略的实例即可。