ノートの端の書き残し

UnityやらC#やら。設計が得意かもしれない。

ゲーム開発においてDIコンテナライブラリは別に正解でもなんでもないという話

DIコンテナの持つ2要素

DIコンテナと呼ばれるライブラリの多くは、「スコープ管理」と「依存性注入」の2つの役割を持っています。これらは同時に利用されることも多いですが、概念としては異なります。

スコープ管理

Unityでの開発であれば、あるシーン内でのみ使用し、そのシーンから抜けたら消えてほしいインスタンスは、そのシーンが「スコープ」となり、プロジェクトがコンテナの活用を基本とした設計になっているならば、シーンにコンテナが1つ用意されており、そのコンテナにインスタンスを登録することになります。コンテナには子コンテナがツリー状に存在できます。

DIコンテナライブラリを利用していなくても、このスコープ管理は非常に重要なので、何らかの手段で必ず実装しておくべきものです。Disposableを活用しましょう。

依存性注入

要するにインスタンスの生成を実際にはDIコンテナが行うことで、依存関係の解決を行いやすくしようぜくらいの話です。これはあると便利なケースは僅かにありますが、別に必須ではありません。単体テストが実質不可能なところが多いゲーム開発では尚更重要度は低いです。

依存性注入が解決できるほとんど唯一の問題

以下のような依存関係を持つクラスA,B,C,Dがあったとします。A,B,C,Dで一つのモジュールを形成していると思ってください。

graph TD;
A-->B
A-->C
B-->D

さらに、DのインスタンスはこのA,B,Cから成るシステムの外部から渡されるとすると、その受け口はAになるでしょう。
ということは、Aは「BにDを渡すために」Dに依存する必要が出てきます。

graph TD;

subgraph module
A-->B
A-->C
B-->D
A-->|Bに渡すために依存|D
end

E[D]-->|渡される|module

これは嫌ですね。Aは要件だけを見ればDを知らなくて済むはずなのに、Bに渡すという実装上の都合のせいでDへの依存が増えました。
こういう依存を無くしてくれるのがDIです。Bに直接Dを渡す神がいればAはDを知らずに済み、例えばBを、Dなんて必要としないB'に置き換えることも容易です。

もちろんDIコンテナを使わなくても実装できますし、やっていることは何も難しくないです。

DIを使えば綺麗になるということはありません。普通に依存関係をシンプルにしたうえで、深いモジュール構造における依存の受け渡しが邪魔になってしまうときに初めてDIを検討する、くらいでよいし、さらにはそのためにDIコンテナライブラリを導入する必要もありません。

参考

www.ulsystems.co.jp

利用コストを理解した上で、DIを利用するようにしましょう。DIが必要とされるのは、通常疎結合が必要とされるところです。そして、アプリケーション内で疎結合が必要とされる箇所はごく限られています。そのため、DIコンテナに登録されるべきクラスはアプリケーション内のごく一部のクラスのはずです。アプリケーションの規模に比べてDIコンテナに登録されているクラス数が多い場合は、本来登録されるべきではないクラスが登録されてしまっている可能性があります。そのようなときは、本当に必要なところでのみDIが利用されているかどうか、見直してください。