考虑到对象封装,getter应该返回一个不可变属性吗?

当getter返回一个属性时,例如返回其他相关对象的List ,该列表和它的对象是否应该是不可变的以防止类之外的代码,更改这些对象的状态,而主要的父对象不知道?

例如,如果一个Contact对象有一个getDetails getter,它返回一个ContactDetails对象List ,那么调用该getter的任何代码:

  1. 可以从该列表中删除ContactDetail对象,而不知道Contact对象。
  2. 可以更改每个ContactDetail对象,而不知道Contact对象。

那我们该怎么做呢? 我们应该只是信任调用代码并返回容易变化的对象,还是努力工作并为每个可变类创建一个不可变类?

这是一个问题,你是否应该在你的代码中“防守”。 如果您是您class级的(唯一)用户并且您相信自己,那么无论如何都不需要不可变性。 但是,如果此代码无论如何都需要工作,或者您不信任您的用户,那么将外部化的所有内容都变为不可变。

也就是说,我创建的大多数属性都是可变的。 偶尔的用户会把这种情况搞得一团糟,但这又是他/她的错,因为有明确certificate突变不应该通过吸气剂接收的可变物体发生。

这取决于具体情况。 如果列表是可变的,那么当List有一个非常好的API时,没有必要用主要类的API来混淆它。

但是,如果主类无法处理突变,那么您将需要返回一个不可变列表 – 列表中的条目本身也可能需要是不可变的。

但是,不要忘记,您可以返回一个自定义List实现,该实现知道如何安全地响应变异请求,无论是通过触发事件还是直接执行任何所需的操作。 事实上,这是使用内部类的好时机的典型例子。

如果您可以控制调用代码,那么最重要的是您所做的选择在所有正确的位置都能很好地记录下来。

在Java中的Collection,List,Set或Map的特定情况下,使用return Collections.unmodifiableList(list);很容易将不可变视图返回给类return Collections.unmodifiableList(list);

当然,如果仍然可以修改后备数据,那么您需要制作列表的完整副本。

Joshua Bloch在他的优秀“有效Java”一书中说,在返回这样的东西时,你应该总是制作防御性副本。 这可能有点极端,特别是如果ContactDetails对象不是Cloneable,但它始终是安全的方式。 如果有疑问总是支持代码安全性而不是性能 – 除非分析表明克隆是一个真正的性能瓶颈。

实际上,您可以添加几种级别的保护。 您可以简单地返回该成员,这实际上是为您的类的内部提供任何其他类访问权限。 非常不安全,但公平地做了很多。 如果要更改内部结构以便将ContactDetails存储在Set中,它也会在以后引起麻烦。 您可以返回一个新创建的列表,其中包含对内部列表中相同对象的引用。 这样更安全 – 另一个类无法删除或添加到列表中,但它可以修改现有对象。 第三,返回一个新创建的列表,其中包含ContactDetails对象的副本。 这是安全的方式,但可能很昂贵。

我会这样做更好。 根本不返回列表 – 而是在列表上返回迭代器 。 这样您就不必创建新列表(List有一个获取迭代器的方法),但外部类不能修改列表。 它仍然可以修改项目,除非您编写自己的迭代器,根据需要克隆元素。 如果您稍后在内部切换到使用另一个集合,它仍然可以返回迭代器,因此不需要进行外部更改。

真的,取决于背景。 但一般来说,是的,应该尽可能编写防御代码(返回数组副本 ,返回集合中readonly包装等)。 无论如何,应该清楚地记录下来

我过去常常返回列表的只读版本,或者至少是一份副本。 但是列表中包含的每个对象都必须是可编辑的,除非它们在设计上是不可变的。

我想你会发现每个gettable都是不可变的非常罕见。

您可以做的是在这些对象中更改属性时触发事件。 也不是完美的解决方案。

文档可能是最实用的解决方案;)

您的首要任务应该是遵守得墨忒耳法则或“告诉不要问”; 告诉对象实例要做什么,例如

 contact.print( printer ) ; // or contact.show( new Dialog() ) ; // or contactList.findByName( searchName ).print( printer ) ; 

面向对象的代码告诉对象做事。 程序代码获取信息然后对该信息起作用。 要求一个对象揭示其内部细节打破封装,它是程序代码,而不是声音OO编程,并且Will已经说它是一个有缺陷的设计。

如果遵循Demeter法则,则通过其定义的界面发生对象状态的任何变化,因此已知并控制副作用。 你的问题消失了。

当我开始时,我仍然受到隐藏你的数据OO PRINCIPALS LOL的影响。 如果有人改变了属性暴露的一个对象的状态,我会坐下来思考会发生什么。 我应该只为外部来电者阅读吗? 我根本不应该暴露它们吗?

collections品将这些焦虑带到了极致。 我的意思是,当我不看的时候,有人可以删除集合中的所有对象!

我终于意识到,如果你的对象对它们外部可见的属性和它们的类型有如此严格的依赖性,那么如果有人在一个不好的地方接触它们你会兴旺发达,那么你的架构是有缺陷的。

有充分的理由使您的外部属性只读,其类型不可变。 但这是一个极端的案例,而不是典型的案例,imho。

首先,setter和getter表示OO不好。 一般来说,OO的想法是你要求对象为你做点什么。 设置和获取是相反的。 Sun应该已经找到了实现Java bean的其他方法,这样人们就不会选择这种模式并认为它是“正确的”。

其次,你拥有的每个对象本身都应该是一个世界 – 通常,如果你打算使用setter和getters,它们应该返回相当安全的独立对象。 这些对象可能是也可能不是不可变的,因为它们只是一流的对象。 另一种可能性是它们返回始终不可变的本机类型。 所以说“定居者和吸气者应该返回一些不可改变的东西”并没有多大意义。

至于自己创建不可变对象,除非你有充分的理由不这样做(最终应该是默认值,“mutable”应该是一个覆盖默认值的关键字),你实际上应该总是将对象中的成员设置为final。 这意味着只要有可能,对象就是不可变的。

至于你可能传递的预定义的准对象事物,我建议你用自己的方法将诸如集合和值组之类的东西一起包装到他们自己的类中。 我几乎从不传递一个不受保护的集合,因为你没有提供任何指导/帮助,如果使用精心设计的对象应该是显而易见的。 安全性也是一个因素,因为允许某人访问您class级内的集合使得几乎不可能确保该class级始终有效。