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 }