一、使用继承可能不完美
二、使用接口也可能不完美
虽然Flyable与Quackable可以解决「一部分」的问题(不会再有会飞的橡皮鸭),但是却造成代码无法复用,这只能算是从一个恶梦跳进另一个恶梦。
三、解决思路
把会变化的部分取出并「封装」起来,好让其他部分不会受到影响。代码变化之后,出其不意的部分变得很少,系统变得更有弹性。
从现在开始,鸭子的行为将被放在分开的类中,此类专门提供某行为的实现。这样,鸭子类就不再需要知道行为的实现细节。
我们利用接口代表每个行为,比方说,FlyBehavior与QuackBehavior,而行为的每个实现都必须实现这些接口之一。所以这次鸭子类不会负责实现Flying与Quacking接口,反而是由其他类专门实现FlyBehavior与QuackBehavior,这就称为「行为」类。由行为类实现行为接口,而不是由Duck类实现行为接口。
这样的作法迥异于以往,以前的作法是:行为是继承Duck超类的具体实现而来,或是继承某个接口并由子类自行实现而来。这两种作法都是依赖于「实现」,我们被实现绑得死死的,没办法更改行为(除非写更多代码)。
在我们的新设计中,鸭子的子类将使用接口(FlyBehavior与QuackBehavior)所表示的行为,所以实际的「实现」不会被绑死在鸭子的子类中。(换句话说,特定的实现代码位于实现FlyBehavior与QuakcBehavior的特定类中)。
「针对接口编程」真正的意思是「针对超类型(supertype)编程」。这里所谓的「接口」有多个含意,接口是一个「概念」,也是一种Java的interface构造。你可以在不涉及Javainterface的情况下,「针对接口编程」,关键就在多态。利用多态,程序可以针对超类型编程,执行时会根据实际状况执行到真正的行为,不会被绑死在超类型的行为上。「针对超类型编程」这句话,可以更明确地说成「变量的声明类型,应该是超类型,通常是一个抽象类或者是一个接口,如此,只要是具体实现此超类型的类所产生的对象,都可以指定给这个变量;这也意味着,声明类时,不用理会以后执行时的真正对象类型!」
在此,我们有两个接口,FlyBehavior和QuackBehavior,还有它们对应的类,负责实现具体的行为:
四、整合鸭子行为
五、使用实例化变量给接口类型赋值
动态改变行为: