🕴️

なぜDIコンテナを使うのか(Unity編)

2 min read

たまに、なぜDIコンテナを使うのかという質問を受けて、疎結合がこうとか依存関係がどうとか説明することがあるのですが、いまいちうまく伝えられないので、ここで整理してみようと思います。

基本的にプログラミングの話は中盤になるまで出てこないので半分ポエムです。なお、Unity以外のDIに関しては門外漢なので、ここでは触れません。

コンポーネント間の依存関係をプログラムのソースコードから排除するために、外部の設定ファイルなどでオブジェクトを注入できるようにするソフトウェアパターンである。
Wikipediaより

DIについては、このように定義されていますが、これを見て即座に意図がわかるなら、この記事を読む必要がありません。DIコンテナとは、このDIを提供するためのフレームワークのことです。

わからんという人のために、もっと簡単な例で説明できないか考えてみることにします。

ここに片付けられない人がいるとします。片付けられない人は、どこに何が置いてあるのか忘れてしまっているため、探しものに時間がかかってしまいます。

片付けられない人は、この状況をなんとかするために、執事のおじさんを雇います。

執事のおじさんは、彼の持ち物をうまいこと整理してくれて、彼が必要な物を即座に見つけてきてくれます。執事のおじさんは彼の欲しい物をよく知っているので「あれを取って」と言うだけで、目当ての物を探し当ててくれます。

執事のおじさんは、大きな箱の中に彼の物を管理しています。

部屋が複数ある場合は、部屋ごとに異なる箱を置いて物を管理します。

執事のおじさんは有能ですが、若干融通が効かない所もあり、居間に置いてある携帯ゲーム機を寝室には持ってきてくれません。執事のおじさんは、部屋から部屋へと物を動かすことに快く思っていません。片付けられない人と執事のおじさんは、このようにして部屋を綺麗に保ちます。

DIは、この片付けられない人と執事のおじさんとの関係で説明できます。

片付けられない人は、どこに何があるのかわからない複雑怪奇なプログラムのことを指し、執事のおじさんはDIコンテナのことを指しています。

片付けられない人は、どの部屋で何が必要か執事のおじさんに指示し、執事のおじさんは、その指示に従って箱(コンテナ)に必要な物を収納します。この作業は、ZenjectにおけるContainer.Bind()、VContainerにおけるbuilder.Register()を指しています。

片付けられない人は、欲しい物があったときに、具体的な名前か、「あれ」といった抽象的な表現で、執事のおじさんに指示します。これは具象クラスやインターフェースを指しています。執事のおじさんは、箱から目当ての物を見つけてきて彼に渡します。この作業をインジェクションと呼びます。

目当ての物がどこかの箱に入ってたとしても、指定した箱に入っていなかった場合、執事のおじさんは、何もしてくれません。このような箱ごとに物を整理する仕組みをZenjectではContext、VContainerではScopeと呼んでいます。ただし、執事のおじさんにうまいこと指示してあげることによって、他の箱から物を探してきてもらうことはできるかもしれません。

(実際はもう少し複雑な仕組みになっていますが、例えるのが難しいのでご容赦くださいw)

ここで、なぜDIコンテナを使うのかという質問に戻るのですが、この質問は、執事のおじさんを雇うことで幸せになれるのかという質問に置き換えることができそうです。そもそも自分で整理整頓できる優秀な人は執事のおじさんを雇う必要がないし、部屋が少ない、あるいは物が少ない人も、執事のおじさんを雇う必要がありません。逆に執事のおじさんがいることによって部屋が息苦しくなることさえあるでしょう。執事のおじさんを必要としているのは、すでに多くの部屋が散らかっている人や、これから部屋が散らかることを予想できている人かもしれません。

また、執事のおじさんを雇うには一定のコストがかかります。ZenjectやVContainerは、オープンソースプロダクトなので無料で使うことはできますが、使ったことがない場合は、学習コストという形でプロジェクト全体のコストに上乗せされます。

なぜ、DIコンテナを使うのかという質問に対しては、DIコンテナを使うことによってプロジェクトの生産性が上がり、それは学習コストを加味してなお有益であると答えるべきでしょうが、うまく伝わらない場合は、このような執事のおじさんの例えで説明するのも良いかもしれません。