我试图了解可变对象与不可变对象。使用可变对象会受到很多负面影响(例如,从方法中返回字符串数组),但我无法理解这样做的负面影响。使用可变对象的最佳实践是什么?你应该尽可能避免它们吗?
嗯,这有几个方面。
没有引用标识的可变对象有时会导致错误。例如,考虑一个Person具有基于值的方法的 bean equals:
Person
equals
Map<Person, String> map = ...
Person p = new Person(); map.put(p, “Hey, there!”);
p.setName(“Daniel”); map.get(p); // => null
Person当用作键时,该实例在映射中“丢失”,因为它的hashCode相等性基于可变值。这些值在地图之外发生了变化,所有的散列都过时了。理论家喜欢在这一点上大谈特谈,但在实践中我并没有发现它是一个太大的问题。
hashCode
另一个方面是代码的逻辑“合理性”。这是一个很难定义的术语,涵盖了从可读性到流畅性的所有内容。通常,您应该能够查看一段代码并轻松理解它的作用。但比这更重要的是,你应该能够说服自己它做的事情是 正确 的。当对象可以在不同的代码“域”中独立更改时,有时很难跟踪什么在哪里以及为什么会发生(“远处的诡异动作”)。这是一个更难举例说明的概念,但在更大、更复杂的架构中经常会遇到这种情况。
最后,可变对象在并发情况下是 杀手。 每当您从不同的线程访问可变对象时,您都必须处理锁定问题。 这会降低 吞吐量并使您的代码更难维护。一个足够复杂的系统使这个问题变得不成比例,以至于几乎无法维护(即使对于并发专家)。
不可变对象(尤其是不可变集合)避免了所有这些问题。一旦你弄清楚它们是如何工作的,你的代码就会发展成更容易阅读、更容易维护并且不太可能以奇怪和不可预测的方式失败的东西。不可变对象甚至更容易测试,不仅因为它们易于模拟,而且因为它们倾向于强制执行的代码模式。简而言之,它们都是很好的练习!
话虽如此,我在这件事上几乎不是狂热者。当一切都是不可变的时,有些问题就不能很好地建模。但是我确实认为您应该尝试将尽可能多的代码推向那个方向,当然假设您使用的语言使这成为一个站得住脚的观点(C/C++ 使这变得非常困难,Java 也是如此) . 简而言之:优势在某种程度上取决于您的问题,但我倾向于更喜欢不变性。