2

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

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

我想到了两种方法:

  1. 创建一个包含所有不可变方法的接口“Thing”(只读)并创建一个包含设置器的接口“MutableThing”。

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

我更喜欢第一个,因为我认为它更干净,设计得更好,但我有一个问题。我在“事物”接口中有 addListener(l) 和 removeListener(l),因此视图层可以将自己注册为某些模型对象的侦听器。但是在“Thing”接口中使用这两种方法,它本身就没有意义了。如果没有实际更改数据的方法,为什么接口能够注册通知数据更改的侦听器?我可以将这些方法放在“MutableThing”接口中,但视图层只能访问“Thing”接口,不能将自己注册为监听器。

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

你有什么建议?

谢谢!

4

4 回答 4

3

如果没有实际更改数据的方法,为什么接口能够注册通知数据更改的侦听器?

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

于 2009-10-20T10:58:18.113 回答
1

我会进行修改后的第一次尝试。

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

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

于 2009-10-20T11:18:23.707 回答
1

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

于 2011-06-20T19:17:53.100 回答
0

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

关于听众的东西:当然有几种方法可以做到这一点。您可以将其与ThingandMutableThing接口分开,例如使用 Java 的java.util.Observable类和java.util.Observer接口(或您自己编写的类似的东西 - 这些类不是类型安全的,因为它们来自泛型添加到 Java 之前)。

于 2009-10-20T10:57:17.557 回答