首页 > 代码库 > 策略模式

策略模式

定义

策略模式定义了算法族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。

类图


设计原则

    1.找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。

      2.针对接口编程,而不是针对实现编程。

      3.多用组合,少用继承。   

举例分析

    设计一个模拟鸭子游戏,游戏中会出现各种(木头鸭、橡皮鸭等等)鸭子,一边游泳(swim),一边叫(quack)。系统类图如下:


去年,公司的竞争压力加剧,此类游戏很多。公司开会决定要对游戏进行升级,要求有些鸭子会飞。程序要Joe是这样设计的,如下:


       程序完成后,当在股东会议上演示的时候,有很多“橡皮鸭”居然会飞。(方法覆盖可以解决此问题,如果有100个不同类型的鸭子,就得重写100次。)

在超类Duck中加上fly()方法,就会导致所有的子类都具备fly(),连那些不该具备fly()的子类也无法免除。

Joe认识到继承可能不是答案,于是Joe有设计了一种方法,如下:



        Joe的上司看了这个设计后,这真是一个超笨的注意,你没有发现这么一来重复的代码会变很多吗?(所有需要quack()fly()方法的鸭子都去重复实现这两个方法)如果你认为覆盖几个方法就算是差劲,那么对于100个Duck的子类都要修改一下飞行(fly())的行为,你又怎么说。

       我们知道,并不是所有的鸭子都会飞、会叫,所以继承不是正确的方法。但是虽然上面的使用Flyable接口的方法,可以解决部分问题(不再有会飞的橡皮鸭子),但是这个解决方案却彻底破坏了重用,它带来了另一个维护的噩梦!甚至,在会飞的鸭子中,飞行的动作可能还有多种变化......。

     呵呵,如果是你你会怎么办?

      仔细思考一下,因为鸭子的行为会在子类里不断的改变,Flyable接口解决了只有会飞的鸭子才实现Flyable接口,但是这种方法无法达到代码复用。这就需要我们的第一个设计原则“分开变化和不会变化的部分”。

      我们知道Duck类内的fly()和quack()会随着鸭子的不同而改变。我们需要做的就是把fly()quack()行为从Duck基类里隔离出来。我们希望一切能有弹性,能够指定行为到鸭子实类。例如:我们可能想要实例化一个新的野鸭实例,并且给它初始化一个特殊类型的飞行行为(飞行能力比较强)。换句话说,我们应该在鸭子类中包含设定行为的方法,这样就可以在“运行时”动态的“改变”野鸭子的飞行行为。有了这样的目标,看看第二条设计原则“针对接口编程,而不是针对实现编程”。

       ”针对接口编程“,关键就在于多态。利用多态,程序可以针对超类编程,执行时会根据实际情况执行到真正的行为,不会被绑死在超类的行为上。

       根据面向接口编程的设计原则,我们应该用接口来隔离鸭子问题中变化的部分,也就是鸭子的不稳定的行为(fly()quack())。我们要用一个FlyBehavior接口表示鸭子的飞行行为,QuackBehavior接口表示鸭子的叫声行为。如下面的类图:


          这样的设计,可以让飞行和呱呱叫的行为被其他的对象复用,因为这些行为已经于鸭子类无关了。二我们可以新增一些行为,不会影响到既有的行为类,也不会影响“使用”到飞行行为类的鸭子类。

        说的有点多,接下来来实现这种设计:

         1.在Duck类中加入两个实例变量,分别是“FlyBehavior”与“QuackBehavior”。每个鸭子对象都将会动态的设置这些变量,以在运行时引用正确的行为类型。

     2.我们还要把fly()quack()方法从Duck类里移除,因为我们已经把这些行为到FlyBehaviorQuackBehavior接口里了。

     3.在鸭子的超类中设定SetQuackBehavior()SetFlyBehavior()那么就可以在运行时动态改变鸭子的行为了。

详细设计类图


 源码 

 http://download.csdn.net/detail/allen_gang/7492871

参考Head First Design Patterns