下面的类,它能够表示圆形或者矩形。
public class Figure {
enum Shape { RECTANGLE,CIRCLE };
//Tag field - the shape of this figure
final Shape shape;
//These fields are used only if shape is RECTANGLE
double length;
double width;
//This field is used only if shape is CIRCLE
double radius;
//Constructor for circle
Figure(double redius){
shape=Shape.CIRCLE;
this.radius=radius;
}
//Constructor for rectangle
public Figure (double lenght,double width) {
shape=Shape.RECTANGLE;
this.length=lenght;
this.width=width;
}
double area(){
switch (shape){
case RECTANGLE:return length*width;
case CIRCLE:return Math.PI*(radius * radius);
default:throw new AssertionError();
}
}
}
这种标签类(tagged class)有着许多的缺点。它们中充斥着样板代码,包括枚举声明、标签域以及条件语句。
多个实现乱七八糟地挤在了单个类中,破坏了可读性。
增加了内存的占用,因为实例承担着属于其他风格的不相关域。
域不能做成final的,除非构造器初始化相关的域,如果不需要初始化相关于,那就会需要增加更多的样板代码。
无法给标签类添加风格,除非可以修改它的源文件,如果一定要添加风格,就必须记得给每个条件语句都添加一个条件,否则类就会在运行时失败。
实例的数据类型没有提供任何关于其风格的线索。
标签类过于冗长、容易出错,并且效率低下。
怎么来表示多种风格对象的单个数据类型?
子类化。标签类正是类层次的一种简单的仿效。
既然标签类的问题那么多,那么这么把标签类转换为类层次?
1、将标签类中的方法(需要用标签来区别的方法)定义成抽象类中的抽象方法。
2、在子类中定义属于该类的数据域。
如下,将Figure标签类
abstract class Figure{
abstract double area();
}
class Circle extends Figure{
final double radius;
Circle(double radius){this.radius=radius;}
double area(){return Math.PI*(radius*radius);}
}
class Rectangle extends Figure{
final double length;
final double width;
Rectangle(double length,double width){
this.length=length;
this.width=width;
}
double area(){return length*length;}
}
将成类层次后,就纠正了标签类的所有缺点。同时方便程序员独立地扩展层次结构,并且不用访问根类的源代码就能相互操作。
类层次还有一个好处,增强灵活性,方便编译时类型检查。类层次能够很好的反映类型之间本质上的层次关系。