私はゲームに取り組んでおり (独自の物理エンジンを作成しています)、優れた設計に従ってゲームを作成しようとしています。最近、見つけにくいバグに遭遇することが多く、これらのバグを見つけやすくするために、単体テストを作成しようとしています。私のコンポーネントの多くが、特にgame
モジュールに密結合されているため、これを行うのは困難でした。
私のgame
モジュールでは、現在のゲームの状態、時間、ゲーム イベントなどを保持するシングルトン クラスのインスタンスをエクスポートします。しかし、これを読んだ後、この結合を減らす方法を調査した結果、次のようにクラスを書き直すことにしました。それはもはやシングルトンではありません。
アイデアは、GameState
クラスを使用し、これらのオブジェクトをあらゆる場所に渡すことです。これにより、単体テストがテストの最小限の状態を作成できるようになります。ほとんどの関数はゲーム状態の関数になり、新しいゲーム状態を返します。ただし、いくつかの設計上の問題に遭遇しました。
私のエンティティ オブジェクトの位置と速度は、現在の時間に従って計算される Python プロパティです。これは、これを関数として書き換えずに単純にオブジェクトを渡すことができないことを意味しGameState
ます (厄介な構文になります)。コード例:
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」オブジェクトを使用することは良い考えでしょうか?
1 つの問題は、現在のゲームの状態を更新するためのインターフェイスです (update
メソッドを定義しましたが、これは奇妙なインターフェイスのようです)。また、グローバル変数の問題は予測不可能なプログラムの状態であることを考えると、これは実際にはそれを防ぐことにはなりません。
- クラスのプロパティに
game
依存関係を「注入」する別の方法はありますか?Entity
position
理想的には、物事をシンプルに保ちたいと思っています。クラスは非常に抽象的に聞こえます(GameStateGetter
実装が単純であっても)。私のプロパティが暗黙的に現在の状態を渡すことができればいいのですが。
ご協力いただきありがとうございます。