1. 前言
设计模式的存在,主要是方便代码的可扩展性和提高代码的复用性。在变化和稳定之间寻找平衡点。
2. 从鸭子说起
策略模式,书中是以鸭子为例,进行介绍的。
假设鸭子的父类为:
Duck
quack()
swim()
display()
//鸭子的其他方法
image.png
因 为 每 一 种 鸭 子 的外 观 都 不 同 , 所 以display()方法是抽象的,每个鸭子子类负责实现自己的display。
3. 鸭子要飞了
现在需要给鸭子们,增加一个飞的动作。
新的需求来了,这时候我们要加入鸭子会飞的功能。鸭子本来有翅膀,没问题,在超类里加入一个fly方法,让所有的子类继承就好了,
Duck
quack()
swim()
display()
fly() //新加入的飞行方法
//鸭子的其他方法
确实,所有的鸭子都会飞了,然而系统里还有一些橡皮鸭子,它们也会飞了,这是不符合常理的!!!
我们忽略了一件事情,并不是所有子类都会飞,某些不会飞的也继承了此方法。
注意:对代码所做的局部修改,影响层面可不只是局部。
当涉及"维护"时,为了"复用"目的而使用继承,结局并不完美。
3.1 第一个解决办法—子类覆盖
我们可以把橡皮鸭中的fly方法覆盖掉
RubberDuck
quack(){//吱吱叫}
display(){//橡皮鸭}
fly(){
//覆盖,什么也不做
}
这样好像确实解决了橡皮鸭的问题,但如果我们以后每次加入不会飞的鸭子,都要这样检查一遍,就失去了复用的意义了。
3.2 第二个解决办法---利用接口
我们可以把fly方法从超类中取出来,放进一个Flyable接口中,这样一来,只有会飞的鸭子才实现此接口。
这样会带来很多重复代码。
4. 重新看待问题
现在我们知道使用继承并不能很好地解决问题,因为鸭子的行为在子类里不断地改变,并且让所有的子类都有这些行为是不恰当的。
Flyable与Quackable接口一开始似乎还挺不错,解决了问题(只有会飞的鸭子才继承Flyable),但是Java接口不具有实现代码,所以继承接口无法达到代码的复用。这意味着: 无论何时你需要修改某个行为,你必须得往下追踪并在每一个定义此行为的类中修改它,一不小心,可能会造成新的错误!
幸运的是,有一个设计原则,恰好适用于此状况。
设计原则: 找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
在变化和稳定之间寻找平衡点。
好,该是把鸭子的行为从Duck类中取出的时候了!
5. 重新设计鸭子
我们提取出两组类,一个是fly,一个是quack,每一组类实现各自的动作,可以指定特定类型的飞行行为给鸭子,让它们动态的去改变就好了。
设计原则:针对接口编程,而不是针对实现编程。
我们利用接口代表每个行为,行为的每个实现都将实现其中的一个接口。
image.png这么一来,有了继承的“复用”好处,却没有继承所带来的包袱。
6. 实现鸭子行为
首先,在Duck类中“加入两个实例变量”,分别为“flyBehavior”与“quack Behavior”,声明为接口类型(而不是具体类实现类型),每个鸭子对象都会动态地设置这些变量以在运行时引用正确的行为类型(例如:FlyWithWings、Squeak等)。
我们也必须将Duck类与其所有子类中的fly()与quack()删除,因为这些行为已经被搬到FlyBehavior与QuackBehavior类中了。
我们用两个相似的方法performFly()和performQuack()取代Duck类中的fly()与quack()。
//父类
Duck
FlyBehavior flyBehavior
QuackBehavior quackBehavior
performQuack(){ quackBehavior.quack(); }
swim()
display()
performFly()
//某个子类
public class MallardDuck extends Duck {
public MallardDuck() {
quackBehavior = new Quack();
flyBehavior = new FlyWithWings();
}
public void display() {
System.out.println(“I’m a real Mallard duck”);
}
}
7. 总结
当你将两个类结合起来使用,如同本例一般,这就是组合(composition)。这种做法和“继承”不同的地方在于,鸭子的行为不是继承来的,而是和适当的行为对象“组合”来的。
如你所见,使用组合建立系统具有很大的弹性,不仅可将算法族封装成类,更可以“在运行时动态地改变行为”,只要组合的行为对象符合正确的接口标准即可。
设计原则:多用组合,少用继承。
策略模式
定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
网友评论