通过继承进行设计2007-05-28 yycnet.yeah.net yyc译学习了多形性的知识后,由于多形性是如此“聪明”的一种工具,所以看起来似乎所有东西都应该继承。但假如过度使用继承技术,也会使自己的设计变得不必要地复杂起来。事实上,当我们以一个现成类为基础建立一个新类时,如首先选择继承,会使情况变得异常复杂。
一个更好的思路是首先选择“合成”——如果不能十分确定自己应使用哪一个。合成不会强迫我们的程序设计进入继承的分级结构中。同时,合成显得更加灵活,因为可以动态选择一种类型(以及行为),而继承要求在编译期间准确地知道一种类型。下面这个例子对此进行了阐释:
//: Transmogrify.java// Dynamically changing the behavior of// an object via composition.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通过合成技术允许它自己的状态发生变化。在这种情况下,那种状态的改变同时也产生了行为的变化。