在一个新的Java项目中,我尝试尽可能多地应用最佳实践。我遇到问题的是不变性。虽然我理解了这个概念并且已经构建了一些不可变的类,但我现在来到了一个类,我认为将它作为一个可变类更合适。
主要问题是我想在某些情况下隐藏类的可变部分,因此在我的情况下,MVC的视图层不能直接修改对象,但必须通过其控制器。
我想到了两种方法:
创建一个接口“Thing”,其中包含所有不可变的方法(只读),并创建一个具有setter的接口“MutableThing”。
将所有方法放在一个接口中,并通过将对象包装为Collections.unmodifiableList(obj)方法来限制对变异方法的访问,以便在访问变异方法时抛出异常。
我更喜欢第一个,因为我认为它更干净,设计更好,但我有一个问题。我在“Thing”接口中有addListener(l)和removeListener(l),因此视图层可以将自己注册为某些模型对象的侦听器。但是在“Thing”界面中使用这两种方法它就不再有意义了。如果没有方法可以实际更改数据,为什么接口能够注册信号数据发生变化的侦听器?我可以将这些方法放在“MutableThing”接口中,但视图层只能访问“Thing”接口,并且无法将自己注册为侦听器。
这不起作用的原因只是因为听众,视图层实际上是否负责将自己注册为模型上的监听器?如果控制器可以以某种方式(可以访问“MutableThing”)进行,那么就不会有问题,但我不知道如何实现它。
你会建议什么?
谢谢!
答案 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方法。
关于听众的事情:有几种方法可以做到这一点。您可以将它与Thing
和MutableThing
接口分开,例如使用Java的java.util.Observable
类和java.util.Observer
接口(或者您自己编写的类似内容 - 这些类不是类型-safe,因为他们是在将泛型添加到Java之前的。)