马戏团问题
现在程序员小明需要设计一个关于马戏团的程序,最初,马戏团只有一个人,也只有一个动物(猴子),因此,小明很快就设计出来了:
没过多久,马戏团又来了一只猴子,会表演跳舞,于是小明将程序改成了:
后来,马戏团的猴子越来越多,小明发现,每次来了一种会不同表演的猴子,都必须重新增加相应的类,并覆盖相应的perform()方法,小明发现这样程序这样下去会变得不好维护:
- 代码在多个子类中重复(有其他会表演跳舞的动物)
- 运行时行为不容易改变(比如有一只什么都不会表演的猴子学会了戴帽子)
- 牵一发动全身(修改父类的perform()方法,会造成不该改变的猴子改变)
- 很难知道猴子所有的表演
此时,小明的主管告诉小明,设计或者修改程序时,我们遵循一个原则:找出类中的变化部分,将它独立出来
。小明首先想到了接口,用接口去表示猴子不同的表演,然后让不同的猴子去实现不同表演,但是仔细一想,上面的1)2)还是不可避免。这时,小明的主管说,把猴子的表演单独成一个类,然后和猴子通过组合模式进行组合,然后猴子的表演行为用其他一些类去实现,并且要记住:面向接口编程
。小明经过深思熟虑,设计出了新的程序:
这样,我们再增加猴子时,只需要增加不同的表演实现类即可,如果来了其他会跳舞的动物,我们也可以复用表演实用类。那么主管给说小明说的面向接口编程又体现在哪里呢?注意Animal,与它组合的并不是具体实现的表演类,而是一个抽象出来的表演行为类(PerformBehavior),这也是设计模式中的依赖倒置原则
定义
你刚刚看到的就是策略模式,定义:定义了方法族,分别封装起来,让他们相互之间可以替换,此模式让算法的变化独立于使用算法的客户
,那么上面的表演行为就是一些列算法,而猴子就是使用它的客户,算法可以自己新增、修改而并不需要修改客户。我们也可以从上面的例子得出一个结论:组合比继承更好(有一个比是一个好)。
角色
- 客户:方法的调用者
- 抽象策略
- 具体策略
优缺点
优点
- 策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族。恰当使用继承可以把公共的代码转移到父类里面,从而避免重复的代码。
- 策略模式提供了可以替换继承关系的办法。继承可以处理多种算法或行为。如果不是用策略模式,那么使用算法或行为的环境类就可能会有一些子类,每一个子类提供一个不同的算法或行为。但是,这样一来算法或行为的使用者就和算法或行为本身混在一起。决定使用哪一种算法或采取哪一种行为的逻辑就和算法或行为的逻辑混合在一起,从而不可能再独立演化。继承使得动态改变算法或行为变得不可能。
- 使用策略模式可以避免使用多重条件转移语句。多重转移语句不易维护,它把采取哪一种算法或采取哪一种行为的逻辑与算法或行为的逻辑混合在一起,统统列在一个多重转移语句里面,比使用继承的办法还要原始和落后。
缺点:
- 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道所有的算法或行为的情况。
- 策略模式造成很多的策略类,每个具体策略类都会产生一个新类。有时候可以通过把依赖于环境的状态保存到客户端里面,而将策略类设计成可共享的,这样策略类实例可以被不同客户端使用。换言之,可以使用享元模式来减少对象的数量。
啰嗦一下
继承(抽象、多态、封装)是我们的OO思想,我们可以用OO思想写出很多程序,但是为什么会出现设计模式呢?其实,设计模式就是在OO思想之后出现,工程师们一点一点总结出来的,它不是被发明的,而是被发现的。它是为了我们写出更容易扩展、修改和理解的程序,要知道软件开发过程中唯一不变得就是变化,所以设计模式有很多好处:
- 共享词汇,最大化沟通交流的价值
- 有利于程序的变化
- 有利于程序的理解
- 有利于程序的维护
- 有利于程序的复用