如何限制对可变或不可变方法的访问?

时间:2009-10-20 10:28:47

标签: java interface immutability mutable

在一个新的Java项目中,我尝试尽可能多地应用最佳实践。我遇到问题的是不变性。虽然我理解了这个概念并且已经构建了一些不可变的类,但我现在来到了一个类,我认为将它作为一个可变类更合适。

主要问题是我想在某些情况下隐藏类的可变部分,因此在我的情况下,MVC的视图层不能直接修改对象,但必须通过其控制器。

我想到了两种方法:

  1. 创建一个接口“Thing”,其中包含所有不可变的方法(只读),并创建一个具有setter的接口“MutableThing”。

  2. 将所有方法放在一个接口中,并通过将对象包装为Collections.unmodifiableList(obj)方法来限制对变异方法的访问,以便在访问变异方法时抛出异常。

  3. 我更喜欢第一个,因为我认为它更干净,设计更好,但我有一个问题。我在“Thing”接口中有addListener(l)和removeListener(l),因此视图层可以将自己注册为某些模型对象的侦听器。但是在“Thing”界面中使用这两种方法它就不再有意义了。如果没有方法可以实际更改数据,为什么接口能够注册信号数据发生变化的侦听器?我可以将这些方法放在“MutableThing”接口中,但视图层只能访问“Thing”接口,并且无法将自己注册为侦听器。

    这不起作用的原因只是因为听众,视图层实际上是否负责将自己注册为模型上的监听器?如果控制器可以以某种方式(可以访问“MutableThing”)进行,那么就不会有问题,但我不知道如何实现它。

    你会建议什么?

    谢谢!

4 个答案:

答案 0 :(得分:3)

  

如果没有方法可以实际更改数据,为什么接口能够注册信号数据发生变化的侦听器呢?

所有界面状态都是您可以检索以下值。它并不意味着该对象是不可变的。当对象是可变的时,IMO是一个好主意,以确保每个人都知道它,因为不可变对象具有您的对象可能没有的一些属性(线程安全,可以安全地缓存结果等)。你的对象是可变的。你不应该假装它是不可变的。因此,请使用1并在Thing界面上添加/删除。

答案 1 :(得分:1)

我会去修改第一次尝试。

您可以通过包含所有getter的接口来定义不可变量。可变类实现了Immutable接口,并包含所有必需的set方法,而不需要通过接口指定这些方法。在您的代码中,您只能通过Immutable接口访问实例。 您实际需要创建或修改类的代码中的位置,您(通过简单地转换)或通过将这些修改实现放在与可变类相同的包中而使它们可见,您(专门)引用这些可变类。像osgi / eclipse这样的bundle概念在这里可能会有所帮助,因为你可以在隐藏实现等方面找到支持。

此外,您可以使用seal()方法在创建(并修改它们)后密封实例。无论何时调用set方法,都要检查实例是否未密封。这可能有助于防止修改(或在大团队中工作时)。

答案 2 :(得分:1)

正确的方法通常是有三个类和/或接口:ReadableFoo,ImmutableFoo和MutableFoo。后两个独立于第一个派生,意味着当需要ReadableFoo时可以使用其中任何一个,但是当需要ImmutableFoo时只能使用ImmutableFoo,同样使用MutableFoo。 ReadableFoo包含CloneAsMutable,AsMutable和AsImmutable的方法可能会有所帮助。 CloneAsMutable将始终创建一个新的可变对象,其中包含从原始复制的属性。在ReadableFoo上调用AsMutable和AsImmutable将返回原始对象(如果是所需类型)或者从原始对象复制数据的新对象(如果不是)。

答案 3 :(得分:0)

在我看来,你提到的两种可能性都是解决问题的有效方法。关于第一种方法:我假设你的意思是接口MutableThing扩展接口Thing以添加mutator方法。

关于听众的事情:有几种方法可以做到这一点。您可以将它与ThingMutableThing接口分开,例如使用Java的java.util.Observable类和java.util.Observer接口(或者您自己编写的类似内容 - 这些类不是类型-safe,因为他们是在将泛型添加到Java之前的。)