假设有一个模块。它包含一个管理类Foo
,其中包含类Bar
的实例列表。
public class Foo {
private List<Bar> bars;
public doStuff() { ... }
private doOtherStuff() { ... }
// method to get bars
public List<Bar> getBars() { ... }
}
然后你有一个带有公共getter的类Bar
来读取Bar
的状态。该类还包含用于更改其状态的mutators。这些mutator可以是公共的和包级的。 public
mutator的原因是此模块类包含另一个模块(其他包)中其他类使用的状态。因此,Bar
的每个实例都是可变的。
public class Bar {
private int myInt; // simple example, but there are more attributes
// constructor kept on package level
Bar(...) { ... }
// getters on public access
public int getMyInt() { ... }
// change state
public void doThis(int i) { ... }
// update state
void doThat(int j) { ... }
}
我遇到了一个问题,当您要显示Bar
(它有多个属性!)的所有实例的概述时,外部用户(UI)可能无法修改此对象的状态。这是可能的,因为其他模块需要一些public
mutators。
搜索后,似乎我可以使用unmodifiableList(List<? extends T> list)
来提供只读列表。像
public List<Bar> getBars() {
return Collections.unmodifiableList(bars);
}
但是这种方法不起作用,因为这可以确保列表本身是只读的。这意味着您无法向其添加元素。但是,一旦你可以检索一个对象,你仍然可以修改它。
List<Bar> myBars = foo.getBars();
myBars.get(0).doThis(...);
方法doThis()
是公共可访问的,因此用户可以在不允许这样做的情况下更改状态。
(抱歉长篇介绍)
有没有一个很好的方法来解决这个安全问题?当然,我可以使方法doThis
非公开,但是我必须在Foo
中添加方法,以便可以从其他模块修改Bar
。在这种情况下,我必须使用所有模块中的所有公共访问方法来做这件事(有很多......)
我想将unmodifiableList
方法与Bar的clone()
结合起来,就像下一个
public List<Bar> getBars() {
List<Bar> returnList = new ArrayList<Bar>(bars.size());
for (Bar b : bars) {
returnList.add(b.clone()); // clone an instance of bar
}
return Collections.unmodifiableList(returnList);
}
这应该有用,但这是一个好方法吗?特别是当类bars
中的列表Foo
可能是一个巨大的列表时,这可能会在尝试显示概述(克隆每个实例)时减慢运行时间。
或者有一个我不知道的好选择吗?
答案 0 :(得分:0)
让Bar
实现仅提供getter的Immutable Interface,例如ImmutableBar
。将Bar
的实例作为ImmutableBar
传递给GUI。
答案 1 :(得分:0)
从上面澄清我的评论,关于&#34;不可变包装器&#34;:想法是ImmutableBar
实现你的Bar
接口,它拦截修改,例如:
/** Wraps a Bar, but prevents all modification operations. */
public final class ImmutableBar implements Bar {
private final Bar wrapped;
public ImmutableBar(Bar bar) {
wrapped = bar;
}
@Override
public int getMyInt() {
return wrapped.GetMyInt();
}
@Override
public void doThis(int i) {
throw new UnsupportedOperationException("Modifications are not allowed");
}
// etc.
}
警告:编译时不禁止修改,而是在运行时禁止修改。但是,您模仿的行为与您已经使用的Collections#unmodifiableList
相同(在尝试修改集合时也会抛出UnsupportedOperationException
)。所以这可能是一个可行的解决方案。
另外请记住,如果你掌握了包裹的Bar
,对这些实例的修改仍会反映在包装器上。如果您设计了一个API,您可以在其中控制要公开的实例,如果您注意,这应该不是问题。
建议:我仍然更喜欢@muued建议的解决方案,它只提供&#34;读取&#34;公共接口Bar
内的操作,它具有不可变的实现(仅实现来自Bar
的getter)和可变实现(另外提供mutator),MutableBar
和ImmutableBar
。看看Apache Commons&#39; Pair举个例子。这避免了开发过程中的任何歧义,因为不可变实例将不提供任何修改API。
答案 2 :(得分:0)
如果我错了,请纠正我,据我所知,如果创建不可变的话,我们不提供任何setter或修改state方法。如果dothis()方法做同样的事情,我们就无法使它成为不可变的。