我正在开发一款游戏(编写自己的物理引擎),并尝试遵循良好的设计来编写它。最近,我遇到了许多难以发现的错误,为了更容易地捕捉这些错误,我一直在尝试编写单元测试。这很难做到,因为我的很多组件都是紧密耦合的,尤其是与game
模块。
在我的game
模块中,我导出了一个单例类实例,它保存了当前的游戏状态、时间、游戏事件等。然而,在阅读了这篇文章并研究了如何减少这种耦合之后,我决定尝试重写这个类,使得它不再是单例。
这个想法是使用一个GameState
类,并在任何地方传递这些对象,以便单元测试可以为测试创建最小的状态。然后大多数函数只是成为游戏状态的函数,并返回一个新的游戏状态。但是,我遇到了一些设计问题:
我的实体对象的位置和速度是根据当前时间计算的 python 属性。这意味着我不能简单地传入一个GameState
对象而不将其重写为函数(导致 icky 语法)。代码示例:
class Entity:
@property
def position(self):
"""Interpolate the position from the last known position from that time."""
# Here, the game class instance is referenced directly, creating coupling.
dt = game.game_time - self._last_valid_time
# x_f = x_i + v_i*t + 1/2 at^2
return self._position + self._velocity * dt + .5 * self._acceleration * dt**2
我已经对如何解决这个问题进行了一些研究,并遇到了Dependency Injection。本质上,我可以将 GameState 'getter' 对象传递给每个实体的初始化程序。所有 GameState 'getter' 对象都会简单地返回当前状态。示例GameStateGetter
类:
class GameStateGetter:
_CurrentState = None
def update(self, new_state):
GameStateGetter._CurrentState = new_state
def __getattr__(self, name):
# GameState object properties are immutable, so this can't cause issues
return getattr(GameStateGetter._CurrentState, name)
现在我的问题。
- 使用 GameState 'getter' 对象会是个好主意吗?
一个问题是更新当前游戏状态的接口(我已经定义了一个update
方法,但这似乎是一个奇怪的接口)。此外,鉴于全局变量的问题是不可预测的程序状态,这并不能真正阻止这种情况。
- 是否有另一种方法可以将
game
依赖项“注入”到Entity
类的position
属性中?
理想情况下,我想让事情变得简单。一个GameStateGetter
类听起来很抽象(即使实现很简单)。如果我的属性可以隐式传递当前状态,那就太好了。
提前感谢您提供的任何帮助!