如何限制对可变或不可变方法的访问?
在一个新的 Java 项目中,我尝试应用尽可能多的最佳实践。我遇到的问题是不变性。尽管我理解了这个概念并且已经构建了一些不可变的类,但我现在来到了一个我认为将其作为可变类进行处理更合适的类。
主要问题是,在某些情况下我想隐藏类的可变部分,因此在我的情况下,MVC 的视图层无法直接修改对象,而必须通过其控制器。
我想到了两种方法来做到这一点:
创建一个接口“Thing”,其中包含所有不可变方法(只读)并创建一个具有设置器的接口“MutableThing”。
将所有方法放在一个接口中,并通过像 Collections.unmodifyingList(obj) 方法这样包装对象来限制对可变方法的访问,以便在访问可变方法时抛出异常。
我更喜欢第一个,因为我认为它更干净,设计得更好,但我有一个问题。我在“Thing”接口中有 addListener(l) 和removeListener(l),因此视图层可以将自身注册为某些模型对象的侦听器。但对于“Thing”接口中的这两种方法来说,它本身就不再有意义了。如果没有实际更改数据的方法,为什么接口能够注册信号数据更改的侦听器?我可以将这些方法放在“MutableThing”接口中,但视图层只能访问“Thing”接口,无法将自身注册为侦听器。
这不起作用的原因只是因为侦听器,视图层实际上负责将自己注册为模型上的侦听器吗?如果控制器可以以某种方式做到这一点(可以访问“MutableThing”),那么就不会有问题,但我不知道如何实现它。
你有什么建议?
谢谢!
In a new Java project I try to apply as much best practices as possible. The one I'm having problems with is immutability. Although I understood the concept and already built some immutable classes I now came to a class where I think it's more appropriate to do it as a mutable class.
The main problem is that I want to hide the mutable parts of the class in some cases so that in my case the view layer of MVC can't directly modify objects but has to go through its controller.
I thought of 2 ways to do it:
Create an interface "Thing" which has all immutable methods in it (read-only) and create an interface "MutableThing" which has the setters.
Put all methods in one interface and restrict access to mutating methods by wrapping the object like the Collections.unmodifiableList(obj) method so that exceptions are thrown when accessing mutating methods.
I would prefer the first one because I think it's cleaner and more well designed but I have one problem with that. I have addListener(l) and removeListener(l) in the "Thing" interface so the view layer can register itself as a listener to some model objects. But with these two methods in the "Thing" interface it just doesn't make sense on its own anymore. Why would an interface have the ability to register listeners which signal data changes if there is no method to actually change data? I could put these methods in the "MutableThing" interface but the view layer only has access to the "Thing" interface and couldn't register itself as listener.
The point why this isn't working is just because of the listeners, is the view layer actually responsible for registering itself as listener on the model? If the controller could do it somehow (which has access to the "MutableThing") then there wouldn't be a problem but I wouldn't know how to realize it.
What would you suggest?
Thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
所有接口状态是您可以检索以下值。它并不意味着该对象是不可变的。当对象是可变的时,在我看来,确保每个人都知道它是一个好主意,因为不可变对象具有您的对象可能不具有的一些属性(线程安全,可以安全地缓存结果等)。你的对象是可变的。你不应该假装它是不可变的。因此,选择 1 并在事物界面上添加/删除。
All the interface states is that you can retrieve the following values. It does not imply that the object is immutable. When the object is mutable it is IMO a good idea to make sure everyone knows it as immutable objects have some properties that your object might not have (thread safe, safe to cache results etc). Your object is mutable. You should not pretend it is immutable. As such go with 1 and have add/removes on the Thing interface.
我会进行修改后的第一次尝试。
您可以通过包含所有 getter 的接口来定义不可变对象。可变类实现 Immutable 接口并包含所有必需的设置方法,而无需通过接口指定这些方法。在您的代码中,您只能通过 Immutable 接口访问实例。
在代码中实际需要创建或修改类的地方,您(仅)通过简单地转换或通过将这些修改实现与可变类放在同一个包中来使它们可见来引用这些可变类。像 osgi/eclipse 中的捆绑概念在这里可能会有所帮助,因为您可以找到隐藏实现等方面的支持。
此外,一旦创建(并修改了实例),您可以使用 seal() 方法来密封实例。每当调用 set-method 时,您都会检查该实例是否未密封。这可能有助于防止修改(或在大团队中工作时)。
I'd go for a modified first attempt.
You may define your immutables by an interface, which contains all the getters. A mutable class implements the Immutable interface and contains all the required set methods without having these specified by an interface. Within your code you only access instances via the Immutable interface.
The places in your code, where you actually need to create or modify the classes, you (exclusively) refer to those mutable classes by simply casting or by making them visible by putting those modifying implementations in the same package as a the mutable classes. A bundle concept like in osgi/eclipse could be helpful here, because you find support in hiding implementations etc.
Additionally you may use a seal() method to seal instances once you have created (and modified them). Whenever a set-method is called, you check, whether the instance isn't sealed. This might help to prevent modifications (or when working in a big team).
正确的方法通常是拥有三个类和/或接口:ReadableFoo、ImmutableFoo 和 MutableFoo。后两者独立于第一个派生,这意味着当需要 ReadableFoo 时可以使用任何一个,但当需要 ImmutableFoo 时只能使用 ImmutableFoo,对于 MutableFoo 也是如此。 ReadableFoo 包含 CloneAsMutable、AsMutable 和 AsImmutable 的方法可能会有所帮助。 CloneAsMutable 将始终创建一个新的可变对象,其属性是从原始对象复制的。在 ReadableFoo 上调用 AsMutable 和 AsImmutable 将返回原始对象(如果它是所需的类型)或一个包含从原始对象复制的数据的新对象(如果不是)。
The right approach is generally to have three classes and/or interfaces: ReadableFoo, ImmutableFoo, and MutableFoo. The latter two derive independently from the first, meaning that either may be used when a ReadableFoo is required, but only an ImmutableFoo can be used when an ImmutableFoo is required, and likewise with MutableFoo. It may be helpful for ReadableFoo to contain methods for CloneAsMutable, AsMutable, and AsImmutable. CloneAsMutable will always create a new mutable object with properties copied from the original. Calling AsMutable and AsImmutable on a ReadableFoo will either return the original object (if it was the desired type) or a new object with data copied from the original (if it wasn't).
在我看来,你提到的两种可能性都是解决问题的有效途径。关于第一种方法:我假设您的意思是接口
MutableThing
扩展了接口Thing
以添加变异器方法。关于听众的东西:当然有几种方法可以做到这一点。您可以将其与
Thing
和MutableThing
接口分开,例如使用 Java 的java.util.Observable
类和java.util .Observer
接口(或者您自己编写的类似内容 - 这些类不是类型安全的,因为它们来自泛型被添加到 Java 之前)。In my opinion, both of the two possibilities that you mentioned are valid ways to solve the problem. With regard to the first method: I assume you mean that interface
MutableThing
extends interfaceThing
to add the mutator methods.With regard to the listener stuff: There are ofcourse several ways to do this. You could separate this from the
Thing
andMutableThing
interface, for example by using Java'sjava.util.Observable
class andjava.util.Observer
interface (or something similar you write yourself - those classes are not type-safe, because they're from before generics were added to Java).