一、引入
继承的最大弊端:
改变会牵一发而动全身,造成其他子类不想要的改变。
无限的重写方法会让人崩溃。
利用接口:
实现接口,也要重写方法,每个实现类都重写这个方法,代码无法复用,工作量更大。
解决:
将变化的行为和不变的行为分开:把变化的行为独立出来
针对接口编程而不是对实现编程:把变化的接口独立出来,实现不同的行为,不同的实体用到该行为时,也可以复用
超类中加入变化的接口,每个变量会利用多态的方式在运行时引用正确的行为类型(变化的接口的实现类)
public abstract class Duck { FlyBehavior flyBehavior; QuackBehavior quackBehavior; public Duck() { } public void performFly(){ flyBehavior.fly(); } public void performQuack(){ quackBehavior.quack(); } public void swim() { System.out.println("Duck 会游泳"); } public abstract void display(); //抽象的方法,每个鸭子都不一样的外貌 }
实现接口
具体子类继承
public class MallardDuck extends Duck { @Override public void display() { // TODO Auto-generated method stub System.out.println("绿头鸭"); } public MallardDuck() { flyBehavior = new FlyWithWings(); quackBehavior = new Quack(); } }
因为子类继承父类,所以具有接口的实例变量,后面会有更多模式可以用。
仍 请 注 意 , 虽 然 我 们 把 行 为 设 定 成 具体的类(通过实例化类似 Q u a c k
或F l y W i t h W i n g s的行为类,并指定到行为引用变量中),但是还是可以在运行时『轻易
1 public class MiniDuckSimulator { 2 3 public static void main(String[] args) { 4 Duck mallardDuck = new MallardDuck(); 5 mallardDuck.performFly(); // 实现鸭子飞行 6 mallardDuck.performQuack(); // 实现鸭子呱呱叫 7 mallardDuck.display(); // 绿头鸭 8 } 9 10 }
动态的改变行为
在Duck中放开修改行为的公共方法。
1 // 动态设定行为 2 public void setFlyBehavior(FlyBehavior flyBehavior) { 3 this.flyBehavior = flyBehavior; 4 } 5 6 public void setQuackBehavior(QuackBehavior quackBehavior) { 7 this.quackBehavior = quackBehavior; 8 } 9 // 从此以后,我们可以「随时」调用这两个方法改变鸭子的行为。
public class FlyRocketPowered implements FlyBehavior { @Override public void fly() { // TODO Auto-generated method stub System.out.println("火箭动力飞行"); } }
1 public class MiniDuckSimulator { 2 3 public static void main(String[] args) { 4 Duck modelDuck = new ModelDuck(); 5 modelDuck.performFly(); //不会飞 6 modelDuck.setFlyBehavior(new FlyRocketPowered()); 7 modelDuck.performFly(); //火箭动力飞行 8 } 9 }
二、总结
大局观:
鸭子子类继承Duck,飞行行为实现FlyBehavior接口,鸭子叫行为实现QuackBehavior接口。
我们描述鸭子的行为时,不再说成「一组行为」,开始把行为想成是「一族算法」。
算法代表鸭子能做的事(不同的叫法和飞行法)。
特别注意类之间的关系。可以是 IS-A(是一个)、HAS-A(有一个)、IMPLEMENTS(实现)。
有一个的关系:每一个鸭子都有一个FlyBehavior 且有一个 QuackBehavior,鸭子将飞行和叫都委托它们代为处理。
将两个类结合起来使用,如同本例一般就是组合(composition)。这种做法和继承不同的地方在于,鸭子行为不是继承而来,
而是和适当的行为对象组合而来。
本例用到三个设计原则:
1、找出应用中可能变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
2、针对接口编程而不是针对实现编程。
3、多用组合,少用继承。
使用组合建立系统具有很大的弹性,不仅可以将算法族封装成类,更可以『 在 运 行 时 动 态 地 改 变 行为』,
这种模式就是『策略模式』Strategy Pattern定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的用户。