121 Star 1K Fork 302

lin-mt/effective-java-third-edition

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
第16项:在公有类中使用访问方法而非公有域.md 4.21 KB
一键复制 编辑 原始数据 按行查看 历史
lin-mt 提交于 2020-02-16 21:15 . 修订 15、16 项

在公有类中使用访问方法而非公有域

  有时候,可能会编写一些退化类(degenerate classes),没什么用,只是用来集中实例域:

// Degenerate classes like this should not be public!
class Point {
    public double x;
    public double y;
}

  由于这种类的数据域是可以被直接访问的,这些类没有提供封装(encapsulation)(第 15 项)。如果不改变 API,就无法改变他的数据表示法,也无法强加任何约束条件;当字段被访问的时候,无法采取任何辅助的行为。坚持面向对象程序设计的程序猿面对这种类深恶痛绝,认为应该用包含私有字段和公共访问方法(getters)的类代替。对于可变的类来说,应该用包含私有字段和公有设置方法(setters)的类代替:

// Encapsulation of data by accessor methods and mutators
class Point {
    private double x;
    private double y;
    public Point(double x, double y) {
        this.x = x;
        this.y = y;
    }
    public double getX() { return x; }
    public double getY() { return y; }
    public void setX(double x) { this.x = x; }
    public void setY(double y) { this.y = y; }
}

  毫无疑问,说到公有类的时候,坚持面向对象程序设计思想的看法是正确的:如果类可以在它所在的包的外部进行访问,就提供访问方法,以保留将来改变该类的内部表示法的灵活性。如果公有类暴露了它的数据字段,要想在将来改变其内部表示法是不可能的,因为公有类的客户端代码已经遍布各处了。

  然而,一个类是包级私有的,或者是私有的嵌套类,直接暴露它的数据字段并没有本质的错误 ———— 假设这些数据字段确实描述了该类所提供的抽象。这种方法比访问方法的做法更不会产生视觉混乱,虽然客户端代码与该类的内部表示法紧密相连,但是这些代码被限定在包含该类的包中。如果有必要更改表示法,你可以在不触及包外的任何代码的情况下进行更改。对于私有嵌套类,更改的范围进一步限制为封闭类【内部类所在的外围类】中。

  Java 平台库中的几个类违反了公共类不应直接公开字段的建议。显著的例子包括 java.awt 包中的 Point 和 Dimension 类。它们是不值得效仿的例子,相反,这些类应该被当做反面的警告示例。正如第 67 项所述,暴露 Dimension 类内部数据造成了严重的性能问题,这个问题至今仍然存在。

  虽然让公有类直接暴露字段永远不是一个好主意,但如果字段是不可变的,这种做法的危害就比较小一点。如果不改变类的 API,就无法改变这种类的表示法,当字段被读取的时候,你也无法采取辅助操作,但是可以强加约束条件。例如,这个类确保了每个实例都表示一个有效的时间:

// Public class with exposed immutable fields - questionable
public final class Time {
    private static final int HOURS_PER_DAY = 24;
    private static final int MINUTES_PER_HOUR = 60;
    public final int hour;
    public final int minute;
    public Time(int hour, int minute) {
        if (hour < 0 || hour >= HOURS_PER_DAY)
            throw new IllegalArgumentException("Hour: " + hour);
        if (minute < 0 || minute >= MINUTES_PER_HOUR)
            throw new IllegalArgumentException("Min: " + minute);
        this.hour = hour;
        this.minute = minute;
    }
    ... // Remainder omitted
}

  总之,公有类永远都不应该暴露可变字段,但是让公有类暴露不可变的字段其危害比较小。但是,有时候会需要用包级私有的或者私有的嵌套类来暴露字段,无论这个类是可变的还是不可变的。

马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
其他
1
https://gitee.com/lin-mt/effective-java-third-edition.git
git@gitee.com:lin-mt/effective-java-third-edition.git
lin-mt
effective-java-third-edition
effective-java-third-edition
master

搜索帮助

A270a887 8829481 3d7a4017 8829481