设计模式
设计模式
引入
从一个简单的继承场景出发:

目前看来没有任何问题,所有鸭子都会嘎嘎叫、都会游泳。
现在希望鸭子会飞,于是在父类中添加了一个fly方法

但是现在就有问题了,比如我们有一个橡皮鸭,橡皮鸭可不会"fly",那么就需要重写fly方法,假设有很多不会"fly"的鸭子,那么这将引发灾难性的问题,因为有多少类就要重写多少,严重违反了开闭原则。如果之后需要扩展新的子类,并且它也不会"fly",那么又需要重写,增加了系统的维护成本。
又假设,现在加入一个小黄鸭,小黄鸭不会嘎嘎叫,那么就需要重写quark方法为吱吱叫。
又有,添加一个木头鸭子,它既不会"fly"也不会"quark",那么就需要重写两个方法。很明显,并不是所有鸭子都需要父类中的所有方法,这个父类过于臃肿了,直接继承违背了许多设计原则。
这就是设计模式的问题。
如果使用接口呢?我们把父类中的具体方法fly和quark提取出来,变成接口Flyable和Quarkable
这在一定程度上缓解了之前的问题。
上面的情况之所以会发生,是因为变化贯穿软件开发始终,是软件开发中唯一不变的东西。
设计原则
分离变化的与不变的
封装变化的部分,使其与不变的部分分离,方便之后扩展或变更变化的部分而不影响不变的部分。(上面例子中最后分离出的两个接口就利用了该原则,fly和quark是会变的,和不变的display、swim分离)
面向接口而不是实现编程
为了设计的灵活性——既要把行为分配给实例,又可能要动态改变对象的行为,甚至可能在运行时改变,这要求我们必须面向接口编程(从而利用多态)
不要让一个具体的父类去实现一个可能变化的子类行为(比如fly和quark),而是创建一组类,专门去表示一种行为,这些不同的行为依赖于一个接口,比如:

上面这个例子对实际问题做了一些简化,当作示意即可,因为实现类往往包括行为职责和数据职责,上面只体现了行为职责,省去了数据职责,实际情况中可能包括的数据职责有振翅频率、飞行高度、速度、音量等。
有了上面这样的设计,鸭子将会委托它的行为给其它类(飞or叫),而不必再使用任何一个在父类或子类中定义的飞或叫行为,这样就分离了变与不变,也实现了面向接口编程。
- 首先向Duck类添加实例变量

- 实现
performQuark()

- 设置
flyBehavior
和quarkBehavior
实例

- 动态设置行为

- 创建一个新鸭子类——模型鸭

- 创建一个新飞行行为类——火箭飞行

我们就能让一个新的模型鸭以火箭驱动飞行啦!

类图如下:

HAS-A 比 IS-A更好
即优先考虑组合而非继承:
- 组合更灵活
- 组合不仅允许将一组算法封装到类中,还允许我们在运行时改变其行为
接下来就是正式介绍设计模式了。
(•‿•)