继承不能很好的解决问题,因为鸭子的行为在子类中会不断的变化,为了适应这种变化,我们不得不一遍遍的在子类中覆盖父类的方法。
接口也不能解决这个问题,因为接口的代码布不具有复用的特性,如果某一天所有的鸭子都变成叽叽叫,那么,你不得不到所有的子类中一个个的修改。
幸运的是,有一个设计原则适应这种情况:
找出应用可能需要变化之处,把他们独立出来,不要和那些不需要变化的代码混淆在一起。把会变化的部分取出来并封装起来,以便以后可以轻易的改动或者扩充此部分,而不需要影响其他不需要变化的部分。
一、首先,分清楚该应用中变化的和不变化的部分,将他们区分开。然后,将变化的部分fly和quack建立两组类,一组与fly有关,一组和quack有关,每一组实现自己各自的动作。
飞行行为的接口:FlyBehavior.java,具体的飞行的行为都要实现该接口
/** * 具体的飞行的行为都要实现该接口 */ public interface QuackBehavior { void quack(); }用翅膀飞行:FlyWithWings.java /** * 用翅膀飞行的行为 */ public class FlyWithWings implements FlyBehavior{ @Override public void fly() { System.out.println("我用翅膀飞行"); } }
不能飞行:FlyNoWay.java /** * 不能飞行 */ public class FlyNoWay implements FlyBehavior{ @Override public void fly() { //不能飞行,空实现 } }
叫 行为的接口:QuackBehavior.java,具体的叫的行为都要实现该接口
呱呱叫:Quack.java /** * 呱呱叫 */ public class Quack implements QuackBehavior{ @Override public void quack() { System.out.println("我可以呱呱叫"); } } 吱吱叫:Squeak.java
/** * 吱吱叫 */ public class Squeak implements QuackBehavior{ @Override public void quack() { System.out.println("我可以吱吱叫"); } } 不会叫:MuteQuack.java
/** * 不会叫 */ public class Quack implements QuackBehavior{ @Override public void quack() { //空实现 } }
这样就设计好了行为的变化,这样的设计,可以使飞行和呱呱叫的动作被其他的对象复用,因为这些行为已经和鸭子没有关系。任何具备这两种特性的对象都可以利用。
并且,我们可以新增加一些行为,而不会对其他的行为有任何的影响。
二、鸭子的行为都已经封装好了,现在就是要把它们整合到鸭子中来。
1、鸭子的超类:Duck.java
/** * 所有鸭子的父类 */ public class Duck { FlyBehavior flyBehavior;//飞行行为 QuackBehavior quackBehavior;//叫的行为 public void swim() { System.out.println("我可以游泳"); } public void display() { } public void performFly() { flyBehavior.fly(); } public void performQuack() { quackBehavior.quack(); } }2、鸭子的子类
绿头鸭:
/** * 绿头鸭,会飞,会叫 */ public class MallardDuck extends Duck { public MallardDuck() { flyBehavior = new FlyWithWings(); quackBehavior = new Quack(); } @Override public void display() { System.out.println("我是绿头鸭!"); } }红头鸭: /** * 红头鸭,会飞,会叫 */ public class ReadheadDuck extends Duck { public ReadheadDuck() { flyBehavior=new FlyWithWings(); quackBehavior=new Quack(); } @Override public void display() { System.out.println("我是红头鸭!"); } } 橡皮鸭: /** * 橡皮鸭是吱吱叫的,不会飞的, */ public class RubberDuck extends Duck{ public RubberDuck() { flyBehavior=new FlyNoWay(); quackBehavior=new Squeak(); } @Override public void display() { System.out.println("我是橡皮鸭!"); } }
诱饵鸭: /** * 诱饵鸭,不会飞,不会叫 */ public class DecoyDuck extends Duck{ //针对不同的鸭子给与不同的实现 public DecoyDuck() { flyBehavior=new FlyNoWay(); quackBehavior=new MuteQuack(); } @Override public void display() { System.out.println("我是诱饵鸭!"); } }
3、测试鸭子的行为 public class TestClass { public static void main(String[] args) { MallardDuck mallardDuck=new MallardDuck();//绿头鸭 ReadheadDuck readheadDuck=new ReadheadDuck();//红头鸭 RubberDuck rubberDuck=new RubberDuck();//橡皮鸭 DecoyDuck decoyDuck=new DecoyDuck();//诱饵鸭 mallardDuck.display(); mallardDuck.performFly(); mallardDuck.swim(); mallardDuck.performQuack(); System.out.println("-------------------"); readheadDuck.display(); readheadDuck.performFly(); readheadDuck.swim(); readheadDuck.performQuack(); System.out.println("-------------------"); rubberDuck.display(); rubberDuck.performFly(); rubberDuck.performQuack(); rubberDuck.swim(); System.out.println("-------------------"); decoyDuck.display(); decoyDuck.performFly(); decoyDuck.swim(); decoyDuck.performQuack(); } }
4、测试结果: 我是绿头鸭! 我用翅膀飞行 我可以游泳 我会呱呱叫 ------------------- 我是红头鸭! 我用翅膀飞行 我可以游泳 我会呱呱叫 ------------------- 我是橡皮鸭! 我可以吱吱叫 我可以游泳 ------------------- 我是诱饵鸭! 我可以游泳
三、这样的一个鸭子应用的体系就已经设计好了,不管是我们要添加更多的飞行行为,还是叫的行为,直接继承FlyBehaviour接口或者QuackBehavior接口,而不会对其他的部分产生影响。修改某个鸭子的行为,也只要在其构造的方法里面针对不同行为的实现就可以了。
而且我们还可以在运行的时候动态的修改鸭子的行为。只需要在Duck超类中添加两个方法就可以了
public void setFlyBehavior(FlyBehavior flyBehavior){ this.flyBehavior=flyBehavior; } public void setFlyBehavior(QuackBehavior quackBehavior){ this.quackBehavior=quackBehavior; }