我试图弄清楚可变对象与不可变对象之间的关系。使用可变对象会带来很多负面影响(例如,从方法中返回字符串数组),但我很难理解这有什么负面影响。使用可变对象的最佳实践是什么?你应该尽可能避免使用它们吗?
发布于 2008-10-18 07:39:00
好吧,这有几个方面。
没有引用标识的
equals方法的Person bean:Map映射= ...Person p=新的Person();map.put(p,“你好!”);p.setName("Daniel");map.get(p);// =>空
当用作键时,Person实例会在映射中“丢失”,因为它的hashCode和相等是基于可变值的。这些值在映射之外发生了更改,所有的散列都变得过时了。理论家喜欢反复强调这一点,但在实践中,我并没有发现这是一个太大的问题。
不可变对象(更具体地说,不可变集合)可以避免所有这些问题。一旦你了解了它们是如何工作的,你的代码就会发展成更容易阅读、更容易维护、更不可能以奇怪和不可预测的方式失败的东西。不可变对象更容易测试,这不仅是因为它们易于模拟,而且还因为它们倾向于强制执行代码模式。简而言之,它们都是很好的实践!
话虽如此,我在这件事上算不上什么狂热分子。当所有东西都是不可变的时候,有些问题就不能很好地建模。但我确实认为,你应该尽可能多地将你的代码推向这个方向,当然,假设你使用的是一种使这一观点站得住脚的语言(C/C++使这一点变得非常困难,Java也是如此)。简而言之:优点在一定程度上取决于你的问题,但我倾向于不变。
发布于 2013-09-18 12:04:58
不可变对象与不可变集合
在关于可变对象与不可变对象的争论中,一个更好的观点是将不可变性的概念扩展到集合的可能性。不可变对象是通常表示单个数据逻辑结构(例如,不可变字符串)的对象。当引用不可变对象时,该对象的内容将不会更改。
不可变集合是一个永远不变的集合。
当我在一个可变集合上执行操作时,我就地更改了该集合,所有引用了该集合的实体都将看到该更改。
当我在一个不可变的集合上执行一个操作时,一个引用被返回到一个反映这个变化的新集合。引用集合以前版本的所有实体都看不到更改。
聪明的实现不一定需要复制(克隆)整个集合来提供这种不变性。最简单的例子是实现为单链表的堆栈和推送/弹出操作。您可以在新集合中重用前一个集合中的所有节点,只为推送添加单个节点,而不为pop克隆任何节点。另一方面,单链表上的push_tail操作就不那么简单或高效了。
不可变变量与可变变量/引用
一些函数式语言采用对象引用本身的不可变性概念,只允许一个引用赋值。
在Erlang中,
开发的简易性与性能
使用不可变对象的原因几乎总是为了促进副作用、自由编程和对代码的简单推理(特别是在高度并发/并行的环境中)。如果对象是不可变的,您不必担心底层数据会被另一个实体更改。
主要的缺点是性能。这是一篇关于a simple test I did in Java的文章,比较了玩具问题中的一些不可变对象和可变对象。
性能问题在许多应用程序中是没有意义的,但不是全部,这就是为什么许多大型数值包,如Python中的Numpy Array类,允许对大型数组进行就地更新。这对于使用大型矩阵和向量运算的应用程序领域非常重要。这种大数据并行和计算密集型问题通过就地操作实现了极大的加速。
发布于 2008-10-18 07:58:25
不可变对象是一个非常强大的概念。它们消除了试图为所有客户端保持对象/变量一致性的许多负担。
您可以将它们用于低级的非多态对象-比如CPoint类-这些对象主要与值语义一起使用。
或者,您可以将它们用于高级的、多态的接口--比如表示数学函数的IFunction --专门用于对象语义。
最大的优点:不变性+对象语义+智能指针使对象所有权不再是问题,默认情况下,对象的所有客户端都有自己的私有副本。隐含地,这也意味着并发存在时的确定性行为。
缺点:当使用包含大量数据的对象时,内存消耗可能成为一个问题。一个解决方案可能是保持对对象的操作符号化,并进行惰性评估。但是,这可能会导致符号计算链,如果界面未设计为支持符号操作,则可能会对性能产生负面影响。在这种情况下,一定要避免的是从方法返回大量内存。与链式符号操作相结合,这可能会导致大量内存消耗和性能下降。
因此,不可变对象绝对是我思考面向对象设计的主要方式,但它们不是教条。它们为对象的客户端解决了许多问题,但也创建了许多问题,特别是对于实现者。
https://stackoverflow.com/questions/214714
复制相似问题