当前位置: 首页 > 知识库问答 >
问题:

是否有一个用不可变对象编写的具有相关状态变化的程序的例子?

贺轶
2023-03-14

在编写了几年可变对象之后,我在大学课程中被介绍到了Java的不可变和函数式编程的世界。

我也看到网上有很多人在谈论不可变性的好处。

  1. https://www.baeldung.com/java-immutable-object
  2. https://softwareengineering.stackexchange.com/questions/151733/if-immutable-objects-are-good-why-do-people-keep-create-mutable-objects

当程序是一个简单的管道,其中每个对象的状态不会影响另一个对象(如计算器)时,使用不可变性是有意义的。输入进去,结果出来。非常适合与纯函数一起使用。后一个等式永远不会影响前一个等式的结果,因此数据只单向流动。

但一旦程序需要对象之间的任何相关状态变化,我就很难应用它。

想一下一个游戏有三样东西,玩家,房间和物品:

// Psuedo Code
class Item {
    final String name;
}

class Player {
    final ArrayList<Item> inventory;
}

class Room {
    final ArrayList<Item> items;
}
There is a room.
Room contains item.
The player walks into a room.
The player picks up an item.

此时,为了将物品移到玩家的库存中,我必须重新创建整个房间,少一个物品,重新创建玩家,多一个物品。

现在想象一下,如果有一个房间链,每个房间都包含一个指向前一个房间和下一个房间的指针。

// Psuedo Code
class Item {
    final String name;
}

class Player {
    final ArrayList<Item> inventory;
}

class Room {
    final ArrayList<Item> items;
    final Room previous;
    final Room next;
}
There is a room.
Room contains item.
The player walks into a room.
The player picks up an item.

因为所有东西(包括房间指针)都是不可变的,为了改变房间中的一个项目,我必须像前面的例子一样重新创建那个房间。但是因为房间是链接的,并且指向上一个和下一个房间的指针是不可变的,所以我不能将上一个和下一个房间链接到我新创建的房间。我必须重新创建链中的每一个房间。

现在我知道了,在Java中,final关键字指的是指针,而不是对象。这意味着从技术上讲,我可以从ArrayList 添加()删除()项,而无需重新创建整个对象。但这首先就会破坏不变的整个目的。

我是不是说错了?有人能指出一个例子,当对象之间的状态变化相关时,使用不可变编程是有意义的吗?

共有1个答案

司空均
2023-03-14

我建议创建加入类。这些可以让你轻松地抓取房间里的物品,并将其复制到玩家库存中。由于复制的结果,房间项目可能会被删除。至于房间的移动,你可以在下节课使用房间来允许从一个房间移动到另一个房间。这只是一个设计,但它应该适用于场景。

类ItemType{id name.。。}

类项{id名称itemTypeId(链接到itemType)。。。}

教室{id名称。。。}

class Player{id name(可以拆分为first,last和username)。。。}

类RoomNext{id currentRoomId nextRoomId.。。。}

类RoomInventory{id itemId roomId.。。}

类PlayerInventory{id ItemId;playerId.。。}

 类似资料:
  • 问题内容: 我知道不可变对象始终具有相同的状态,即它们实际创建时的状态。它们的不变量由构造函数建立,并且由于它们的状态在构造后不会更改,因此这些不变量始终保持良好状态,这就是为什么可以在多线程环境中安全发布它们的原因。很好,但是由于我们生活在一个动态的世界中,程序的状态不断变化,如果通过不可变的对象构造程序的状态,那么这些对象会给我们带来什么好处? 问题答案: 您已经回答了“这些物品能给我们带来什

  • 我想出了这个类: 这里是用法: 问题: 为什么使用作为返回表达式编译得很好(Z不是instexpr)。 如何调用和方法的对象() ? 中的行为在编译器中是不同的。MSVC可以使用作为模板参数进行编译,而GCC(IdeOne)不会编译它。 对于一个编译器来说< code>constexpr GetX是真正的< code>constexpr,但是对于另一个编译器来说,如果涉及到< code>X Z就不

  • 问题内容: python是否有不可变的列表? 假设我希望具有元素的有序集合的功能,但是我想保证不会改变,那么如何实现呢?列表是有序的,但可以更改。 问题答案: 是。称为。 所以,相反的是一个和可以突变,是,不能。 更多的信息: 不能通过编写实例化一个元素,而是需要编写。这是因为解释器在括号中还有其他用途。 您也可以完全取消括号:与 请注意,元组不是 完全 不可变的列表。单击此处以了解有关列表和元组

  • 问题内容: 静态变量只有实例(也就是说,它们是类的一部分)。例如:Math.pi 有什么办法可以有多个静态变量实例吗?我听说有一些与Classloader相关的东西吗? 问题答案: 如果发现需要一个静态变量的多个实例,则强烈表明您不应该首先使用静态变量。 是的,如果同一类加载到不同的类加载器中,则该类的每个副本将具有其自己的静态变量。但是,唯一可以静态引用这些静态变量的代码将是由同一类加载器加载的

  • 问题内容: 编译器不允许静态方法调用非静态方法。我知道这样做是因为非静态方法通常最终会使用实例变量。 但是拥有一个不使用实例变量的非静态方法是否有意义。如果我们的行为不影响实例状态或不受实例状态的影响,则不应将这种方法标记为静态。 问题答案: 通常,没有。如果该方法不涉及任何实例状态,则没有理由将其绑定到实例。 当然,静态方法不能被继承或覆盖,因此这是您明显希望拥有一个不使用实例状态的实例方法的明

  • 问题内容: 这是Java Concurrency in Practice中的一句话 共享的只读对象包括不可变的和实际上不可变的对象。 不变对象和有效不变对象之间有什么区别? 问题答案: 不可扩展且其字段全部为自身且不可变的类的实例是不可变的。 由于其方法的详细信息而无法更改其字段的类的实例实际上是不可变的。例如: 的某些实例实际上是不可变的,而有些则不是。 另一个例子是零长度数组。它们实际上是不可

  • 问题内容: 我想确保根据Java内存模型正确理解“有效不可变对象”的行为。 假设我们有一个可变的类,我们希望将其发布为有效的不可变的类: 我们执行以下操作: 问题是 :Java内存模型是否保证所有线程都必须具有? 根据 Java Concurrency In Practice, 这应该是正确的,但是如果我错了,请更正我。 3.5.3。安全出版惯用语 为了安全地发布对象,必须同时使对该对象的引用和该

  • 我想确保我正确理解根据Java内存模型的‘有效不可变对象’行为。 假设我们有一个可变类,我们希望将它发布为一个有效不可变的类: 我们执行以下操作: 问题是:Java内存模型是否保证所有线程必须具有? 根据Java并发实践,这应该是正确的,但如果我错了,请纠正我。 3.5.3.安全发布习语 要安全地发布对象,必须同时使对对象的引用和对象的状态对其他线程可见。通过以下方法可以安全地发布正确构造的对象: