我无法了解我们需要不可变类的情况。
你遇到过这样的要求吗?或者你能给我们任何我们应该使用这种模式的真实例子吗?
原文由 Rakesh Juyal 发布,翻译遵循 CC BY-SA 4.0 许可协议
我无法了解我们需要不可变类的情况。
你遇到过这样的要求吗?或者你能给我们任何我们应该使用这种模式的真实例子吗?
原文由 Rakesh Juyal 发布,翻译遵循 CC BY-SA 4.0 许可协议
不可变类通常更 易于设计、实现和正确使用。一个例子是 String: java.lang.String
的实现比 std::string
在 C++ 中的实现要简单得多,主要是因为它的不变性。
不可变性产生特别大差异的一个特定领域是并发性: 不可变对象可以在多个线程之间安全地共享,而可变对象必须通过仔细的设计和实现实现线程安全——通常这远非一项微不足道的任务。
更新: Effective Java 2nd Edition 详细解决了这个问题 - 请参阅 _第 15 项:最小化可变性_。
另请参阅这些相关帖子:
原文由 Péter Török 发布,翻译遵循 CC BY-SA 3.0 许可协议
15 回答8.2k 阅读
8 回答6k 阅读
1 回答4.1k 阅读✓ 已解决
3 回答2.2k 阅读✓ 已解决
2 回答3.2k 阅读
2 回答3.9k 阅读
1 回答2.2k 阅读✓ 已解决
其他答案似乎过于专注于解释为什么不变性很好。它非常好,我会尽可能使用它。 _但是,那不是你的问题_。我将逐点回答您的问题,以确保您获得所需的答案和示例。
“需要”在这里是一个相对的术语。不可变类是一种设计模式,与任何范例/模式/工具一样,它可以使构建软件更容易。同样,在 OO 范式出现之前编写了大量代码,但将我算在 “需要” OO 的程序员中。不可变类,如 OO,并不是严格 需要 的,但我会表现得像我需要它们一样。
如果您没有以正确的视角查看问题域中的对象,您可能看不到对不可变对象的 _需求_。如果您不熟悉何时有利地使用它们,可能很容易认为问题域 不需要 任何不可变类。
我经常使用不可变类,将问题域中的给定对象视为 值或固定实例。这个概念有时取决于视角或视点,但理想情况下,很容易切换到正确的视角来识别好的候选对象。
通过确保阅读各种书籍/在线文章以形成对如何思考不可变类的良好认识,您可以更好地了解不可变对象 真正有用 的地方(如果不是绝对必要的话)。一篇可以帮助您入门的好文章是 Java 理论与实践:变异还是不变异?
我将尝试在下面给出几个示例,说明人们如何从不同的视角(可变与不可变)查看对象,以阐明我所说的视角的含义。
既然你问的是真实的例子,我会给你一些,但首先,让我们从一些经典的例子开始。
经典值对象
字符串和整数通常被认为是值。因此,发现 String 类和 Integer 包装类(以及其他包装类)在 Java 中是不可变的也就不足为奇了。颜色通常被认为是一个值,因此是不可变的 Color 类。
反例
相比之下,汽车通常不被视为有价值的对象。为汽车建模通常意味着创建一个具有不断变化的状态(里程表、速度、燃油水平等)的类。但是,在某些领域,它可能是一个值对象。例如,一辆汽车(或特别是汽车型号)可能被视为应用程序中的一个值对象,用于为给定车辆查找合适的机油。
扑克牌
写过扑克牌程序吗?我做到了。我可以将扑克牌表示为具有可变花色和等级的可变对象。抽牌手牌可能是 5 个固定实例,其中更换我手中的第 5 张牌意味着通过改变其花色和等级 ivars 将第 5 张扑克牌实例变异为一张新牌。
但是,我倾向于将扑克牌视为一种不变的对象,一旦创建,它的花色和等级就固定不变。我的抽牌手牌是 5 个实例,更换我手中的一张牌将涉及丢弃其中一个实例并将一个新的随机实例添加到我的手上。
地图投影
最后一个例子是当我处理一些地图代码时,地图可以在各种 投影 中显示自己。原始代码让地图使用固定但可变的投影实例(如上面的可变扑克牌)。更改地图投影意味着改变地图投影实例的 ivars(投影类型、中心点、缩放等)。
但是,如果我将投影视为不可变值或固定实例,我觉得设计会更简单。更改地图投影意味着让地图引用不同的投影实例,而不是改变地图的固定投影实例。这也使得捕获诸如
MERCATOR_WORLD_VIEW
类的命名投影变得更加简单。