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

Java 如何限制对可变或不可变方法的访问?,java,interface,immutability,mutable,Java,Interface,Immutability,Mutable,在一个新的Java项目中,我尝试应用尽可能多的最佳实践。我遇到的问题是不变性。虽然我理解了这个概念,并且已经构建了一些不可变的类,但我现在来到了一个类,我认为作为一个可变的类更合适 主要的问题是,我想在某些情况下隐藏类的可变部分,这样MVC的视图层就不能直接修改对象,而必须通过它的控制器 我想到了两种方法: 创建一个包含所有不可变方法(只读)的接口“Thing”,并创建一个包含setter的接口“MutableThing” 将所有方法放在一个接口中,并通过像Collections.unmodif

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

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

我想到了两种方法:

  • 创建一个包含所有不可变方法(只读)的接口“Thing”,并创建一个包含setter的接口“MutableThing”

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

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

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

    你有什么建议


    谢谢

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

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

    如果没有实际更改数据的方法,为什么接口能够注册监听器来更改信号数据


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

    我会选择修改后的第一次尝试

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


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

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

    我本来要提到这一点,但您已经这样做了。就其本身而言,这不是问题的答案,否则+1+1-显然OP将“不可变”与“可变但不使用此API”混淆。理解这种困惑将有助于OP设计出一个连贯的API。感谢您的回答和评论!好的,我理解你混淆这两个术语的意思,那么,将接口命名为可变*或不可变*通常是一个坏主意,因为它应该属于它的实现吗?我认为是这样。然而,如何处理不应在视图层中公开的setter?离开MutableThing?就我个人而言,我不喜欢在类名中添加这样的元数据(如果是接口的话,那么是可变的)。如果这是真的,那就更糟了。取决于这个类有多重,我只需要接口和实现(使用集合)。实际上我也想到了这一点,但不确定这是否是一个伟大的OO设计?好的,如果所有这些都发生在一个包中,那没关系,但是如果是这样呢