设计模式-策略模式

来源:互联网 发布:js toggle 编辑:程序博客网 时间:2024/05/29 04:43

今天看完了head first设计模式,现在每一种模式按照自己的理解整理一遍~ 首先是策略模式

1.设计背景

假如你刚为老板完成了一套相当成功的模拟鸭子游戏,游戏中会出现各种鸭子。你采用面向对象的思想来设计这些鸭子,使用一个超类,并让所有鸭子继承这个类。超类中有三个方法,两个具体方法分别是quack呱呱叫和swim游泳,还有一个抽象方法display,因为每个鸭子外观不同,所以定义抽象方法让子类去实现。类图如下所示:


2.新的需求---尝试更改

上面的设计虽好,但是这时候老板有了新的需求,它要给每个鸭子添加一个fly方法,让鸭子有飞的行为。嘿嘿!既然所有鸭子都要有fly方法, 我们直接把fly放在超类里面就好啦,也就是在超类定义具体方法。看起来没什么问题,但是上面提到过,我们的鸭子实在多,其中也包括橡皮鸭子,而橡皮鸭子不会飞,因此继承fly方法会导致橡皮鸭子飞起来,难以接受!

于是你想到让不会飞的鸭子全都重写超类的fly方法,但是这个实在太麻烦啦!以后每次增加一种鸭子,都必须考虑鸭子会不会飞,然后要考虑重写父类方法。而且目前只有一个fly方法,但是其实并不是所有鸭子都会呱呱叫,现在就是fly和quark两个方法的问题,未来还可能更多。那么利用接口怎么样呢?把fly和quark的行为抽象出来,分别做成两个接口,会飞的鸭子实现flyable接口,会呱呱叫的实现quarkable接口。类图如下:


这个设计虽然麻烦了一点,但是基本上可以满足我们的需求啦。但是你有没有发现,这样一来我们的fly和quark方法代码完全没法复用了。因为每个实现接口的鸭子类只能自己实现自己的fly和quark方法。这样也很麻烦,我们需要可以复用的代码,也需要更加简化的类图。

3.解决方法-策略模式

记住一个设计原则:把应用中可能经常变化的部分独立出来,不要和那些稳定不变的代码混在一起。这样可以方便我们扩展功能。

在这个问题中,我们知道fly和quark方法会随着鸭子的不同而改变,因此我们可以把fly和quark的行为从Duck超类中分离出来,用一组新的类来代表每个行为。我们用接口代表每个行为,而行为的每个实现都将实现其中一个接口。如下图:


我们这样的设计可以让飞行和呱呱叫的动作被其他对象复用,因为这些已经和鸭子无关了。我们甚至可以很轻松的增加新的行为,只需要另外定义接口就可以啦!

接下来我们在Duck超类中加入两个实例变量,如下图:


然后把飞行和呱呱叫的行为委托给对象的实例对象(也就是Duck的属性),我们必须在构造方法中实例化这两个属性,还必须实现每个属性的setter和getter方法。这样的设计才是正宗的策略模式。从此以后鸭子的呱呱叫行为就像下面一样委托给了quackBehavior属性对象了。而我们可以使用setter方法随时替换这个属性对象。使用策略模式,以后不管增加再多的行为(只需要设计相应的行为接口和实现类,然后给Duck加上一个属性对象和相应的方法,稍微修改构造方法),或者增加再多的鸭子我们都可以迅速完成,几乎不需要更改原有代码,这样的设计十分具有弹性,我们再也不怕该需求啦!


4.策略模式的正式定义

策略模式定义了算法族,分别封装起来,让他们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。
解释一下这个比较抽象难懂的定义。所谓的算法族就是我们上面定义的各种行为接口和实现类,我们把算法族封装起来,独立于使用算法的客户(鸭子),并且每个算法可以随时替换(通过鸭子每个属性的getter和setter方法)。策略模式适用于需要经常改变算法或者扩充算法的情况,如果经常改变的只有算法,策略模式无疑是很好的选择!

5.这个模式涉及的设计原则总结

(1)封装变化!
(2)多用组合,少用继承!
我们使用组合把飞行和呱呱叫的行为委托给其他类,避免了继承带来的困扰!
(3)针对接口编程,不针对实现编程!
我们在Duck超类中定义的两个属性其实都是接口,但是他们在实例化的时候被实例化成具体实现类,利用多态的特性,在运行时我们对接口类型对象的方法调用其实调用的是具体的实现类。这就是针对接口编程的意思,针对接口编程也就是针对超类编程,这点在java中应用非常广泛!


原创粉丝点击