Java 2D 游戏:在哪里放置 "global" 非最终变量

Java 2D game: Where to put "global" non-final variables

我正在开发一款 Java 2D 游戏,我正在为此使用 AWT 图形、JFrame 和 JPanel。我想知道我应该在哪里存储一些非最终但全局可访问的(我需要能够 read/change 来自其他 Object/Class 的这些变量)变量来协调例如游戏面板的宽度和高度,我希望能够在游戏内的一个小设置菜单中进行更改。

目前,我将这些变量存储在我的大多数其他 classes 实现的单独接口中,但这意味着我无法更改变量,因为它们都是最终变量。

据我所知,我有两个选择:

将所有这些变量作为静态的非最终变量放入我的主游戏 class 并通过 Game.PanelWidth

访问它们

为这些变量创建一个单独的 class 并像这样访问它们:Variables.PanelWidth,其中 "Variables" 将是新的 class' 名称。

哪种方法更好,还是我应该使用完全不同的方法?

如果不仔细阅读您的代码,就很难提供具体的回复。

一般建议:

At the moment, I am storing these variables in a separate interface which most of my other classes implement, but this means I cannot change the variables as they are all final.

使用继承来访问您的设置不是一个好主意。仅出于这个原因,我认为您正在考虑的任何一个选项都是一种改进,因为它使用组合而不是继承。

您应该考虑应用 Single Responsibility Principle

  • 谁负责管理您的游戏设置?可能是 Properties 个对象。
  • 谁负责构造这样一个对象?也许 main 方法。

如果您应用此原则,您的 class 几乎不需要访问 PanelWidth 属性。

具体建议:

Java提供了Propertiesclass。 class 是线程安全的,并且在 loading/storing 文件属性时易于使用。

来自Java文档:

This class is thread-safe: multiple threads can share a single Properties object without the need for external synchronization.

与其创建静态属性对象,不如考虑在 main 方法中构造属性对象(基本上,穷人的依赖注入)

选择在很大程度上取决于您,应该在复杂性和应用程序的实际需求之间取得平衡。

您提到的接口是一个已知的反模式 常量接口 - 通常不受欢迎,因为您在继承树和命名空间中随处散布着本应保持封装的信息。虽然它确实有效,但在小型项目中它可能是可以接受的。

单独的 class 是更好的方法,它至少解决了命名空间垃圾问题。将其构造函数设置为 final 以防止意外的 subclassing。

一般来说,静态成员可以在小项目中工作,但如果项目变大并且变量并不是真正的应用程序全局变量,而只是用例全局变量,这可能是不可能的。重构这样的案例可以在以后创造很多工作。

使用 "global" 设置的实际实例 class 从一开始就阻止了这种情况,代价是您需要将实例传递到需要访问的地方(例如作为构造函数参数).

接下来是线程和与这些全局变量更改 通信的问题。除非您的应用程序 运行 在 线程中(纯事件驱动的 swing 应用程序就是这种情况),否则您不能简单地更改值并期望更改为在每个依赖站点 正确地 生效(想象一下刚刚阅读了 panelWidth,但在阅读 panelHeight 之前设置了新的尺寸)。您需要一种方法来防止这些情况。这么简单的成员就出来了。您需要 get/set 方法来确保只读取完整的信息并且相关值的写入是原子的。

可以通过将相关值封装到一个复合对象中来确保原子性,例如:您有一个 panelDimension,而不是 panelWidth,panelHeight。没有提供单个值的 getters/setters。您只能获取整个维度(用于读取)或将其替换为新维度(用于写入)。实际成员可以是易失性的、AtomicReference 变体或通过使 getter/setter 同步来保护。

为了正确地将更改传达给每个依赖站点,您可能需要一些通知机制,因此整个全局状态或它的各个部分可能需要能够注册侦听器并在更改时通知这些侦听器(同样线程问题是需要考虑,因为侦听器回调通常在进行更改的线程上实现,这可能需要在调用的侦听器中考虑)。