Effective Java笔记第三章类和接口第八节类层次优于标签类

本文探讨了如何通过类层次结构替换标签类以提高代码的清晰度、可读性和效率。标签类存在的问题包括样板代码多、易出错、内存占用大等,而采用子类型化可以避免这些问题,确保每个类型都有独立的类,数据域更专注,且编译器能确保正确初始化。类层次结构还允许反映类型间的关系,增强灵活性并进行更好的编译时类型检查。建议在遇到标签类时考虑重构为类层次结构。
摘要由CSDN通过智能技术生成

Effective Java笔记第三章类和接口

第八节类层次优于标签类

1.有时候,可能会遇到带有两种甚至更多种风格的实例的类,并包含表示实例风格的标签域。比如说下面这个例子:

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;

    //These fields are used only if shape is CIRCLE
    //这些字段仅在形状为圆形时使用
    double radius;

    //Constructor of circle
    //圆的构造函数
    public Figure(double radius) {
        shape=Shape.CIRCLE;
        this.radius = radius;
    }

    //Constructor of RECTANGLE
    //矩形的构造函数
    public Figure(double length, double width) {
        shape=Shape.RECTANGLE;
        this.length = length;
        this.width = width;
    }

    double area(){
        switch (shape){
            case RECTANGLE:
                return length*width;
            case CIRCLE:
                return Math.PI*(radius*radius);
                default:
                    throw new AssertionError();
        }
    }

    public static void main(String[] args) {
        Figure figure=new Figure(1,2);
        Shape shape = figure.shape;
        System.out.println(shape);
        double area = figure.area();
        System.out.println(area);
    }

}

这种标签类有许多缺点。他们充斥着样板代码,包括枚举声明,标签域以及条件语句。犹豫多个实现乱七八糟的挤在了单个类中,破坏了可读性。内存占用增加了,因为实例承担着属于其他风格的不相关的域。域不能做成是final的,除非构造器初始化了不相关的域,产生更多的样板代码。构造器必须不借助编译器,来设置标签域,并初始化正确的数据域:如果初始化了错误的域,程序就会在运行时失败。无法给标签类添加风格,除非可以修改他的源文件。如果一定要添加风格,就必须记得给每条件语句都添加一个条件,否则类就会在运行时失败。最后,实例的数据类型没有提供任何关于其风格的线索。总之一句话来说:标签类过于冗长,容易出错,并且效率低下。

2.面向对象的语言提供了更好的方法来定义能表示多种风格对象的单个数据类型:子类型化。标签类正是类层次的一种简单的仿效。

3.为了将标签类转变成类层次,首先要为标签类中的每个方法都定义一个包含抽象方法的抽象类,这每个方法的行为都依赖与标签值。这个抽象类是类层次的根。如果还有其他的方法其行为不依赖于标签的值,就把这样的方法放在这个类中。同样的,如果所有的方法都用到了某些数据域,就应该把他们放在这个类中。接下来,为每种原始标签类都定义根类的具体子类。在每个子类中都包含特定于该类型的数据域。同时在每个子类中还包括针对根类中每个抽象方法的相应实现。下面我们对上面的例子进行改造:

public abstract class FigureImprove {

    abstract double area();

}

class Circle extends FigureImprove {

    final double radius;

    Circle(double radius) {
        this.radius = radius;
    }

    @Override
    double area() {
        return Math.PI * (radius * radius);
    }

}

class Rectangle extends FigureImprove {

    final double length;
    final double width;

    public Rectangle(double length, double width) {
        this.length = length;
        this.width = width;
    }

    @Override
    double area() {
        return length * width;
    }

}

这段代码简单且清楚,没有包含在原来的版本中所见到的所有样板代码。每个类型的实现都配有自己的类,这些类都没有受到不相关的数据域的拖累。所有的域都是final的。编译器确保每个类的构造器都初始化他的数据域,对于根类中声明的每个抽象方法,都确保有一个实现。这样就杜绝了由于遗漏switch case而导致运行时失败的可能性。多个程序员可以独立的扩展层次结构,并且不用访问根类的源代码就能相互操作。每种类型都有一种相关的独立的数据类型,允许程序员指明变量的类型,限制变量,并将参数输入到特殊的类型。

4.类层次的另一种好处在于,他们可以用来反映类型之间本质上的层次关系,有助于增强灵活性,并进行更好的编译时类型检查。比如说:

class Square extends Rectangle {

    Square(double side) {
        super(side, side);
    }

}

5.简而言之,标签类很少有适用的时候。当你想要编写一个包含显示标签域的类时,就应该考虑一下,这个标签是否可以被取消,这个类是否可以用类层次来代替。当你遇到一个包含标签域的现有类时,就要考虑将他重构到一个层次结构中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值