有时可能会遇到带有两种甚至更多种风格的实例的类,并包含表示实例风格的标签(tag)域。例如,以下面这个类为例,它能够表示圆形或者矩形:
这种标签类(tagged class)有许多缺点。它们中充斥着样板代码,包括枚举声明、标签域以及条件语句。由于多个实现乱七八糟地挤在单个类中,破坏了可读性。由于实例承担着属于其他风格的不相关的域,因此内存占用也增加了。域不能做成 final 的,除非构造器初始化了不相关的域,产生了更多的样板代码。构造器必须不借助编译器来设置标签域,并初始化正确的数据域:如果初始化了错误的域,程序就会在运行时失败。无法给标签类添加风格,除非可以修改它的源文件。如果一定要添加风格,就必须记得给每个条件语句都添加一个条件,否则类就会在运行时失败。最后,实例的数据类型没有提供任何关于其风格的线索。一句话,标签类过于冗长、容易出错,并且效率低下。
幸运的是,面向对象的语言(如 Java)提供了其他更好的方法来定义能表示多种风格对象的单个数据类型:子类型化(subtyping)。标签类正是对类层次的一种简单的仿效。
为了将标签类转变成类层次,首先要为标签类中的每个方法都定义一个包含抽象方法的抽象类,标签类的行为依赖于标签值。在 Figure类中,只有一个这样的方法:area.这个抽象类是类层次的根(root)。如果还有其他的方法其行为不依赖于标签的值,就把这样的方法放在这个类中。同样地,如果所有的方法都用到了某些数据域,就应该把它们放在这个类中。在 Figure 类中,不存在这种类型独立的方法或者数据域。