术语:
退化类(degenerate classes):退化类是对类成员的一种限制形式,只保留了数据域,类似于c语言里的结构体。是一种结构化的数据。
有时候,可能会编写民一些退货类,只是用来集中实例域。
// Degenerate classes like this should not be public!
class Point {
public double x;
public double y;
}
如上这样的类绝不应该声名为public,因为一旦声名为了public,这就变成了API的一部分,客户端可直接操作所有数据域。这些类没有提供封装的功能,如果不改变API,就无法改变它的数据表示法,也无法强加任何约束条件,当被访问的时候,无法采取任何辅助措施,这么多问题,归结原因就是因为如果类声名不当,那么可能会将整个数据域全部暴露给客户端,因此想要以另一种形式展现数据的时候就必须要更改API,而且任何对数据表现形式的改动都将涉及到很多兼容性问题,封闭不当造成的问题统统出现了。坚持面向对象的程序设计人员对这种类深恶痛绝,认为应该用包含私有域和公有访问方法的类来代替,对于可变类来说,应该用包含私有域和仅有设置方法的类代替,也就是应该数据,而提供getter和setter方法:
// 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; }
}
很显然,当谈论到公有类的时候,坚持面向对象设计思想的看法是正确的:如果类可以在它所在的包的外部进行访问是,就提供访问方法,以保留将来改变该类内部表示法的灵活性。如果公有类暴露了它的数据域,要想在将来改变其内部表示法是不可能的,因为公有类的客户端代码已经遍布各处了。
然而,如果类是包级私有的,或者是私有的嵌套类,直接暴露它的数据域并没有什么本质的错误。这种方法比访问方法的做法更不会产生视觉混乱,无论是在类定义中,还是在使用该类的客户端代码中。虽然客户端代码与该类的内部表示法紧密相连, 但是这些代码被限定在包含该类的包中。如果有必要,不改变包之外的任何代码而只改变内部的数据表示法也是可以的。在私有的嵌套类的情况下,改变以的作用范围又进一步限制在外围类中。这样问题变得更加不明显了。
如果域是不可改变的,那么让公有类直接暴露域的害处会比较小一点。但是,如果不改变类的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("Minute: " + minute);
this.hour = hour;
this.minute = minute;
}
// Remainder omitted
}
总之,公有类永远都不应该暴露可变的域,有时候会需要用包级私有的或者私有的嵌套类来暴露域,无论这个类的域是可变的还是不可变的。