当我们以一个现成类为基础建立一个新类时,如首先选择继承,会使情况变得异常复杂。一个更好的思路是首先选择“合成”——如果不能十分确定自己应使用哪一个。合成不会强迫我们的程序设计进入继承的分级结构中。同时,合成显得更加灵活,因为可以动态选择一种类型(以及行为),而继承要求在编译期间准确地知道一种类型。下面这个例子对此进行了阐释:
interface Actor {
void act();
}
class HappyActor implements Actor {
public void act() {
System.out.println("HappyActor");
}
}
class SadActor implements Actor {
public void act() {
System.out.println("SadActor");
}
}
class Stage {
Actor a = new HappyActor();
void change() {
a = new SadActor();
}
void go() {
a.act();
}
}
public class Transmogrify {
public static void main(String[] args) {
Stage s = new Stage();
s.go(); // Prints "HappyActor"
s.change();
s.go(); // Prints "SadActor"
}
}
在这里,一个Stage 对象包含了指向一个Actor 的句柄,后者被初始化成一个 HappyActor对象。这意味着go()会产生特定的行为。但由于句柄在运行期间可以重新与一个不同的对象绑定或结合起来,所以SadActor对象的句柄可在a 中得到替换,然后由go()产生的行为发生改变。这样一来,我们在运行期间就获得了很大的灵活性。与此相反,我们不能在运行期间换用不同的形式来进行继承;它要求在编译期间完全决定下来。一条常规的设计准则是:用继承表达行为间的差异,并用成员变量表达状态的变化。在上述例子中,两者都得到了应用:继承了两个不同的类,用于表达 act()方法的差异;而 Stage通过合成技术允许它自己的状态
发生变化。在这种情况下,那种状态的改变同时也产生了行为的变化。
总之,继承用来定义行为的差异,状态的变化最好用合成来做。