偶然地你可能忍不住编写一个退化的类,除了把实例域分组以外不作为任何目的:
// 像这样的退化的类不应该是公开的!
class Point {
public double x;
public double y;
}
因为这样的类的数据域可以直接访问,所以这些类没有提供封装的优点(条目15)。没有改变API你就不能改变这个表示,你不能实现不变性,而且当访问一个域时你不能采取辅助行动。不妥协的面向对象程序员感觉这种类是诅咒,应该总是用这样的类替代:它有私有域和公开访问子方法(accessor method)(getter),而且有对于可变类的设置方法(mutator)(setter):
// 通过访问子方法和设置方法的数据封装
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 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
}
概括起来,公开类从不应该暴露可变域。尽管仍然有问题,但是公开类暴露不可变域有较低危害。然而,包私有的或者私有的内嵌类暴露不管可变与不可变的域,有时是合适的。
网友评论