メインコンテンツまでスキップ

DIって何 ?

DI(=Dependency Injection) は、オブジェクト指向プログラミングにおける一般的なテクニック/パターンのことです。

あるクラスのコードの中から「委譲先のオブジェクトがどこからやってくるのか?」という情報を取り除き、クラスたちと、それらの関係を結びつける設定とを分けてしまおう、というのが主なコンセプトです。

DIで参照関係を組み立てると、アプリケーションのコードを変更せずともクラス同士の依存関係をつなぎかえることができるので、テスタビリティやある種のメンテナンス性が改善する... とかとか一般的には言われています。

……そんな教科書的な話の全てがクライアントサイドでも強いメリットを持つとも限りませんが、参照関係を結びつけるやりかたに一貫したスタイルを持ち込むことにある種の快適さがあることは確かです。

note

C# では、サーバやコンソールアプリケーションを書くための 設定・ロギング・などなどを整備する標準的な仕組み Generic Host がありますが、これはDIをベースにしたフレームワークとしてデザインされてます。 .NET Core/.NET 5以降、サーバのフレームワークとしてデファクトスタンダードといえる ASP.NET Core もまた、Generic Host を利用したDIベースのスタイルです。

Generic Host をはじめとする Mircosoft.Extensions.* の名前を冠したパッケージは、 dotnet/runtime リポジトリ内で管理されており、 .NET環境やサーバサイドでは標準的な位置付けです。Microsoft.Extensions.DependencyInjection は、DIの基本的な機能を提供している他、DIのインターフェイスを標準化して、実装を他のライブラリと置き換える、といった仕組みも持っています。

C# でDIを使用するプログラミングをするなら要チェックだ!

どんなプログラミングのパラダイムを用いるにせよ、「モジュール結合度は低く」、「モジュール凝集度は高く」、とするのが基本です。ご存知のとおり、オブジェクト指向では オブジェクト を通してこれを行います。

  1. オブジェクトは自身の責務について、ごちゃごちゃした詳細を外から隠蔽する。(カプセル化)
  2. オブジェクトは自身の責務ではない仕事については、別のオブジェクトへ委譲する。

よさそうです。しかし実際にこれを完璧に行おうとするのは意外に簡単ではありません。 あるクラスのコードのなかに別のクラスへの委譲を書こうとすると、単純に考えれば、委譲先のオジェクトの生成方法または取得方法もそこに書くことになりそうです。しかしもう少し考えてみると、それは委譲元が委譲先の実装を選んでしまうことになりますし、委譲先のインターフェイスさえ知っていれば十分な場合でも、委譲元ではコードを書き換えないと実装が交換可能ではありません。 実を言うと、これをカンペキに避けるには外側からオブジェクトの参照を渡してあげるしかありません。DI っていうのはつまりそれを行うパターンのことです。

用語集#

  • DIコンテナ: 参照関係を管理したいオブジェクトたちをまとめて保持する場所のこと。
  • Composition Root: 依存関係についての設定をまとめて記述する場所。これを一箇所にまとめてアプリケーションのコードとは分離することで、各クラスのコードから委譲先を検索したり生成するコードが消える。
  • Auto-wiring: 必要なオブジェクトを外から渡してあげることで、参照関係を自動的に組み立てる機能のこと。普通、DIコンテナはこの機能を持っている。
  • IoC (Inversion of Control = 制御の反転): 制御フローに責任を持つオブジェクトが先に呼び出され、中心的な制御を担う形にすること。ごく素朴なプログラミングでは、処理の起点になるのはユーザ入力や割り込みを受けつける場所になる。一般にアプリケーションフレームワークはそこを反転(Inversion)させて、あくまで制御フローだけに責任を持つオブジェクトから処理がはじまる形にするのが普通。

なぜ Unity にDI ?#

通常、Unityにおいては、 MonoBehaviour が アプリケーションの処理の起点になります。と同時に、画面に表示するものを表現した 「Viewコンポーネント」の役割を持つのもまた MonoBehaviour です。

アプリケーション設計においては、「ドメインロジックとプレゼンテーション(Viewコンポーネント)の分離」がとても大事です。MonoBehaviour が多くの役割(イベントハンドリング、制御フロー、ドメインロジック呼び出し、etc...)を担っていると、この原則が破綻してしまいがちです。

DI の目的のひとつに IoC (制御の反転) があります。DIコンテナを使うと、参照関係の順番についていかようにでも主導権を握れるので、 MonoBehaviour のようなUnityに依存した末端のオブジェクトじゃなくて、自由に定義した C# クラスの方を処理のはじまりにすることができます。 これは、制御フローおよびその他のドメインロジックと、ViewコンポーネントとしてのMonoBehaviourの機能との分離に役立ちます。

Viewコンポーネントは、実行時に生成/破棄されるかもしれないので、寿命が不安定と言えます。一方、制御フローやドメインロジックなどの「機能」を司るオブジェクトは、安定した寿命を持ちます。 一般的に、Viewコンポーネントから 制御フローやドメインロジックを分離することで、オブジェクト同士の参照関係の管理がシンプルになります。

これは 作者がDIを好む主な理由です。