Joe上班的公司做了一套模拟鸭子游戏:SimUDuck。游戏中会出现各种鸭子,一边游泳戏水,一边呱呱叫。此系统的内部设计使用了标准的OO技术,设计了一个鸭子超类(Superclass),并让各种鸭子继承此超类。类图如下:
去年,公司的竞争压力加剧,主管们确定,此模拟程序需要会飞的鸭子(第一次需求)来将竞争者抛在后头。Joe的想法和类图如下(使用继承+重写覆盖-方案1-1):
但是,可怕的问题发生了,所有的鸭子都会飞,尝试用覆盖的方法去解决,但是每次加入一种新鸭子后,都要重写Duck类里的会改变的方法(例如,fly(),quack() ),达不到复用的目的。
利用继承来提供Duck的行为,这会导致A.代码在多个子类中重复,B.运行时的行为不容易改变,C.很难知道所有鸭子的全部行为。D.改变会牵一发动全身,造成其他鸭子不想要的改变。
方案1-2(利用接口):
但是问题又来了:
我们知道,并非“所有”的子类都具有飞行和呱呱叫的行为,所以继承并不是适当的解决方式。虽然Flyable与Quackable可以解决“一部分”问题(不会再有会飞的橡皮鸭),但是却造成了代码无法复用,这只能算是从一个恶梦跳进另一个恶梦。甚至,在会飞的鸭子中,飞行的动作可能还有多种变化……
方案1-3:
如果每次新的需求一来,都会使某方面的代码发生变化,那么你就可以确定,这部分的代码需要被抽出来,和其他稳定的代码有所区分。
为了要分开“变化和不会变化的部分”,我们准备建立两组类(完全远离Duck类),一个是“fly”相关的,一个是“quack”相关的,每组类将实现各自的动作。比方说,我们可能有一个类实现“呱呱叫”,另一个类实现“吱吱叫”,还有一个类实现“安静”。
针对接口编程--针对超类型编程--变量的声明类型应该是超类型 (多态),类图和代码如下:
1 //------飞行行为接口和实现类------- 2 interface FlyBehavior { 3 public void fly(); 4 } 5 6 class FlyWithWings implements FlyBehavior { 7 public void fly() { 8 System.out.println("I'm flying"); 9 } 10 } 11 12 class FlyNoWay implements FlyBehavior { 13 public void fly() { 14 System.out.println("I can't fly"); 15 } 16 } 17 18 //---------叫声行为及实现类-------- 19 interface QuackBehavior { 20 public void quack(); 21 } 22 23 class Quack implements QuackBehavior { 24 public void quack() { 25 System.out.println("Quack"); 26 } 27 } 28 29 class Squeak implements QuackBehavior { 30 public void quack() { 31 System.out.println("Squeak"); 32 } 33 } 34 35 class MuteQuack implements QuackBehavior { 36 public void quack() { 37 System.out.println("Silence"); 38 } 39 } 40 //--------鸭子超类------- 41 42 abstract class Duck { 43 FlyBehavior flyBehavior; 44 QuackBehavior quackBehavior; 45 46 public Duck() { 47 48 } 49 50 public abstract void display(); 51 52 public void performFly() { 53 flyBehavior.fly(); 54 } 55 56 public void performQuack() { 57 quackBehavior.quack(); 58 } 59 60 public void swim() { 61 System.out.println("All ducks float, even decoys"); 62 } 63 } 64 65 //-----绿头鸭类---------- 66 class MallardDuck extends Duck { 67 public MallardDuck() { 68 quackBehavior = new Quack(); 69 flyBehavior = new FlyWithWings(); 70 } 71 72 public void display() { 73 System.out.println("I'm a real Mallard duck"); 74 } 75 } 76 77 //------测试类-------------- 78 public class MiniDuckSimulator { 79 public static void main(String[] args) { 80 Duck mallardDuck = new MallardDuck(); 81 mallardDuck.performFly(); 82 mallardDuck.performQuack(); 83 } 84 }