シングルトンの誘惑に抗う(でもインスタンスのたらい回しはツラすぎる…)
うわぁ… またコンストラクタに引数追加しなきゃ…
ゲームには様々なインフラオブジェクトが必要になる。
レンダラー、ミキサー、ゲームパッド、フォント、セーブデータ、ワールドクロック、etc…
ゲームの根幹を成すものだけでもこれだけあるのに、ここにさらに、進行フラグ、ゲームオブジェクトマネージャ、ブロックフィールド、衝突空間、カメラ、シーンマネージャ、アイテムマネージャ等々のドメイン固有のオブジェクトがガサーッと加わるもんだから、その総数は両手じゃ数えきれない。
こいつらを今、すべてコンストラクタを介してたらい回ししている。正直ツラい。
自分はこんな感じの一節をいったい何回書いたんだ。
this( Renderer renderer, Mixer mixer, Gamepad gemepad, WorldClock worldClock, GameObjectManager gameObjectManager, CollisionZone collisionZone, // ...まだいっぱい続く。 // クラス固有の引数がこの後さらに続く。 ) // nullが入ると困るんで、ちゃんとin契約も書きましょうね~ in (renderer !is null) in (mixer !is null) // ...まだいっぱい続く。 { // おっと、終わりじゃないぜ? メンバ変数に突っ込む作業が残ってる。 _renderer = renderer; _mixer = mixer; // ...まだいっぱい続く。 } // 当然、メンバ変数も山のように… private Renderer _renderer; private Mixer _mixer; // ...
もちろんすべてのクラスが10も20もインフラオブジェクトを必要とするわけではない。体感では平均して5,6個くらいだろうか。
それでもコンストラクタが汚れてソースを読むときの雑音になっている事実は否めない。
何より、なんか新しい機能を追加しようとした際に、その実現に必要なインフラをコンストラクタにいちいち追加しなきゃいけないのが堪らなく面倒である。
当然、コンストラクタを呼び出す側がそのインフラを持っていない場合は、インフラを受け渡すというただそれだけのために、上位階層まで再帰的にコンストラクタを拡張しなきゃいけなくなる。時々なにと戦っているのかわからなくなることがある。
You、シングルトンにしちゃいなよ!
このささやきを何度耳にしただろう?
どうせインスタンスが一つしか存在し得ないものばかりなんだから確かにシングルトンでもいいような気はするのだが、3点ほど気になることがあって踏みとどまっている。
- たらい回しが面倒とか、コンストラクタが汚くなるとか、そういう理由でシングルトンを持ち出すのはマズいでしょ?
- ほぼテスト書けなくなるよ?
- 「シングルトン == グローバル変数 == 言語道断の悪」みたいな観念が出来上がっている。
どうしたもんかなあ。でも静的変数を共有するのはやっぱ良くないよなあ。
インフラを束ねたツールボックス的なクラスを作ればまあ 1. と 2. は解決するんだが、それって結局グローバル変数と何が違うの?と感じるところもあり、どうにも踏ん切りがつかん。落としどころとしてはベターなんだろうけど。
一般的にこういうときってどうするんだろうね?(虚空への問いかけ)