首页 > 代码库 > 策略模式
策略模式
定义
策略模式:定义了算法族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。
类图
设计原则
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类里移除,因为我们已经把这些行为到FlyBehavior和QuackBehavior接口里了。
3.在鸭子的超类中设定SetQuackBehavior()和SetFlyBehavior()那么就可以在运行时动态改变鸭子的行为了。
详细设计类图
源码
http://download.csdn.net/detail/allen_gang/7492871
参考:《Head First Design Patterns》