我从来没有听说过这个词,所以我用谷歌搜索了它。这篇文章解释得很清楚:
http://www.cs.newpaltz.edu/~pletcha/oop_chap5_1.html
暴露代表意味着违反对象的方法控制其状态的规则。例如,如果一个对象有实例变量和修改器来改变它们的值,那么这个对象就有一种控制它的状态的方法。如果我调用foo.setBar(5),那么如果文档说getBar 返回由setBar 设置的值,则foo.getBar() 最好返回5。
我将解释为什么你给出的三个描述中的每一个都暴露了一个对象的表示(或者更一般地说,打破了封装):
不将其类中的变量声明为私有。
这个是最简单的。如果实例变量是公共的,则 JVM 中的任何内容都可以从同一对象/类中的代码外部更改它们的值。如果我们调用foo.setBar(5),然后调用foo.getBar(),我们可能会得到5 以外的值,因为bar 是公共范围,因此其他代码区域可能已经对其进行了变异。
允许对象中的变量引用作为构造函数参数传递给它的对象。
我花了一分钟才理解这一点,但如果您将一个对象及其依赖项视为一个单一的单元,这将是有意义的。
如果Foo 有一个Bar 并且Bar 有一个称为x 的int,那么Foo 可以看到并控制bar 上的x 属性,因为它有一个引用。如果我创建Foo 的实例并在Foo 的构造函数中传递对Bar 实例的引用,则看起来Foo 具有完全控制权。但事实并非如此。示例:
public class Foo {
private Bar bar;
public Foo(Bar bar) {
this.bar = bar;
}
// immutable property - can only be read once this object is instantiated
public Bar getBar() {
return this.bar;
}
}
public class Bar {
private int x;
public Bar(int x) {
this.x = x;
}
public int getX() {
return this.x;
}
public void setX(int x) {
this.x = x;
}
}
// some other java class
Bar bar = new Bar(10);
Foo foo = new Foo(bar);
bar.setX(5);
此代码公开了代表,因为foo 做出了一个关键假设,即它控制bar。注意它对bar 的引用是不可变的。但它并不是一成不变的。只有引用本身是不可变的。创建foo 的代码仍然有对bar 的引用,并且可以在foo 不知道的情况下对其进行变异。
更简单地说,foo 依赖于 bar,并将其视为自身的一部分。但是bar 实际上可以独立改变,因此foo 的状态会在它不知情的情况下间接改变。
允许将存储在对象变量中的引用作为对该对象调用的方法的返回值传递。
这是最容易通过集合来解释的。
public class Foo {
private Collection<Bar> bars = new ArrayList<Bar>();
// immutable property - can only be read once this object is instantiated
public Collection<Bar> getBars() {
return this.bars;
}
public void addBar(Bar bar) {
this.bars.add(bar);
}
public int getBarCount() {
return this.bars.size();
}
}
Foo foo = new Foo();
foo.getBars().add(new Bar(someUnexpectedBar));
System.out.println(foo.getBarCount()); // -> 1
这违反了合同。要添加条形图,您应该致电addBar。这就是暴露该方法的原因。通过返回对getBars 中集合的引用,可以操作底层集合。
起初这似乎微不足道。但是如果Foo 制定了这条规则并且上述用法违反了它,那么如果我想将Foo 重构为此会发生什么(假设是为了性能):
public class Foo {
private Collection<Bar> bars = new LinkedList<Bar>();
private int barCount; // for faster inserts, use a linked list and to maintain fast counts, track the count ourselves by tracking the adds.
// immutable property - can only be read once this object is instantiated
public Collection<Bar> getBars() {
return this.bars;
}
public void addBar(Bar bar) {
this.bars.add(bar);
this.barCount++;
}
public int getBarCount() {
return this.barCount;
}
}
Foo foo = new Foo();
foo.getBars().add(new Bar(someUnexpectedBar));
System.out.println(foo.getBarCount()); // -> 0
从功能上讲,它是相同的。如果您适当地使用其中的方法,则不会有任何区别。但是我们作弊了。我们抓取了底层集合并对其进行了变异。现在getBarCount() 方法返回错误答案 (0)。
解决这个问题的方法是返回一个带有原始副本的新集合。
public Collection<Bar> getBars() {
return new ArrayList<Bar>(this.bars);
}
甚至
public Collection<Bar> getBars() {
return Collections.unmodifiableCollection(this.bars);
}