Strategy(Policy) Pattern
Strategy(Policy) Pattern译名为策略模式,是Gof经典《设计模式》一书中23种模式之一。本文将引用实际项目场景使用策略模式对实际项目进行改造,来阐述策略模式的用途。
下文使用Strategy代替Strategy(Policy) Pattern表述。
Strategy的核心:Strategy允许算法或对象独立于使用它的客户而变化。
Strategy的使用方法:把过程中的易变部分提取到一个单独的“策略”对象中,将规则与它所控制的行为区分开。Strategy实现规则或可替换的过程。Strategy的多种实现表示完成过程的不同方式。
Strategy标准UML:
Edit By: http://yuml.me/0d337825
公司实际项目中的例子:
Edit By:http://yuml.me/1a72b147
如图所示,在AppBase基类里实现了公共的Animation方法,Animation方法实现了AppBase的UI效果,而PhotoApp和VideoApp可以分别覆盖实现自己的OpenApp和CloseApp方法,即重用了公共的部分,又支持不同子类的个性化扩展。从实际使用效果来看,在当时是一个很好的设计。
但是,随着时间的推移,越来越多的用户需求汇集到项目组。主要的需求集中在界面需要更炫的效果,针对需求,项目组丝毫不敢怠慢,研究之后,决定在Base基类里增加一些方法实现并改写Animation就能搞定。所有的继承Base类的子类就拥有了新的UI呈现特性。
一番敲打之后发现,每个子App的效果很不理想,比如根据X、Y轴Scale、根据X轴Rotate等等。最终,大家认识到使用继承不是方法,因为呈现的需求经常在变,今天可以是Modern Style明天直接让你变Ribbon Style后天学360桌面,再之后。。。所以,未来的变化将十分频繁,并且是不可预知的,如果直接靠逐个重写和修改显然无法应对变化,显然这样就混不下去了~
这时很迷惑,为什么屡试不爽的继承,针对新需求的设计实现不断涌入的时候,无法很好的支持重用呢?
在之后,我试图将Animation抽成一个接口,抽象为IAnimation分解成Scale,Rotate。显然如果有几十个App的时候都需要重复实现接口的方法。如果某个方法需要一点修改,可能需要修改多处。显然这个方案可以否决了。
我们知道不是所有的App都需要Scale,Rotate。所以继承不是正确的方法,虽然上面使用了一个接口的方法可以解决部分问题,但是彻底破坏了重用。还有一个问题也很重要,每个App的Scale,Rotate等行为都不一定是一样的。
写到这里,把前奏问题通过实际场景抛出了,挨下来策略模式可以正式登场了。所谓“计划没有变化快”,直面变化才是解决问题的正道,场景中AppBase的行为在子类里持续不断的改变,所以让所有的子类都拥有基类的行为是不适当的,而使用上面的接口的方式,又破坏了代码重用。现在就需要用两个设计原则来解决问题:
1.找到变化并且把它封装起来,稍后你就可以在不影响其它部分的情况下修改或扩展被封装的变化部分。
2.面向接口编程,而不要面向实现编程。
现在动手来改造下:
1.给AppBase类增加两个接口类型的实例变量,scaleBehavior和rotateBehavior,它们实现新的设计里的“缩放”和“旋转”的行为;
2.把Animation方法从AppBase里移除,新增PerformScale()和PerformRotate()方法;
3.考虑何时初始化ScaleBehavior和RotateBehavior变量,新增设置变量值的SetScaleBehavior()和SetRotateBehavior(),这样就可以在运行时动态改变AppBase的行为;
Edit-By:http://yuml.me/d4b87f1a
这种实现就是Strategy,策略模式的应用。
Context:
- 需要使用ConcreteStrategy提供的算法,如上面场景中的ScaleWithX()方法;
- 基类维护一个Strategy的实例;
- 基类负责动态设置运行时Strategy具体的实现算法。如SetScaleBehavior();
- 基类负责跟Strategy之间的交互和数据传递;
Strategy:
定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,Context使用这个接口调用不同的算法,一般使用接口或抽象类实现。
ConcreteStrategy:
实现了Strategy定义的接口,提供具体的算法实现。
每一个设计模式都是针对某一场景的解决实际问题。关于Strategy的优点和场景网上有很多介绍,就不再列举。
Strategy的缺点
因为每个具体策略类(ConcreteStrategy)都会产生一个新类,所以会增加应用程序需要维护的类的数量。
最后,在文中说了两个设计原则,这里再补充一个重要的设计原则:
优先使用对象组合,而非类继承(Favor composition over inheritance)。这句话怎么理解大家可以自行Google本文就不展开了叙述了。
PS:
本文只是引用实际项目作为Strategy讲解的案例示例,针对实际项目的重构,应根据实际的设计要求,不以本文作为任何指引。