
マイケル
みなさんこんにちは!
マイケルです!!
マイケルです!!

エレキベア
こんにちクマ〜〜〜

マイケル
今日はUnityのDIフレームワークである VContainer について
使い方を紹介していくぜ!
使い方を紹介していくぜ!

エレキベア
最近はUnity界隈でDIが流行ってるクマね〜〜〜

マイケル
今回の記事を書くにあたって、下記の公式ページとスライドを参考にさせていただきました!
リファリンスも分かりやすくまとまっているのでおすすめです。
リファリンスも分かりやすくまとまっているのでおすすめです。
About | VContainer
↑VContainerの公式ページ
Unity専用最速DIコンテナVContainer と、UnityにおけるDIの勘所
↑作者本人の説明スライド

エレキベア
シンプルで使いやすそうクマね〜〜〜

マイケル
いきなりDIと言われても分からない方もいると思うので、
最初に 依存関係やDIについて説明した後、
ライブラリの使い方を説明 していこうかと思います!
最初に 依存関係やDIについて説明した後、
ライブラリの使い方を説明 していこうかと思います!
DI(依存性の注入)とは

マイケル
まずはDIとは何か?という部分について解説していきます。
以前、SOLID原則について紹介しましたが、この中で出てくる
「DIP(依存性逆転の原則)」を実現するための手段としてDIが用いられます。
以前、SOLID原則について紹介しましたが、この中で出てくる
「DIP(依存性逆転の原則)」を実現するための手段としてDIが用いられます。

エレキベア
確か 依存関係を逆転させて、抽象に依存させるべき
という原則だったクマね
という原則だったクマね

マイケル
その通り!
依存関係を制御するための手段として、「DI」「DIコンテナ」というものが使われているんだ。
まずはイメージを掴むために簡単なDIの例を見てみよう!
依存関係を制御するための手段として、「DI」「DIコンテナ」というものが使われているんだ。
まずはイメージを掴むために簡単なDIの例を見てみよう!
依存関係とDI

マイケル
まず、下記のようなマイケルクラスがあったとします。
この中では所持モンスターを変数として持っています。
この中では所持モンスターを変数として持っています。
↑マイケルクラスの実装

マイケル
この実装はよく見ると、エレキベアクラスをそのまま使っているため、
エレキベア以外のモンスターを所持することはできません。
エレキベア以外のモンスターを所持することはできません。

エレキベア
誰が所持モンスターになんてなるかクマ

マイケル
この状態のことをマイケルクラスはエレキベアクラスに依存していると言われます。
UML図で書くと下記のようになります。依存の向きが矢印の向きですね。
UML図で書くと下記のようになります。依存の向きが矢印の向きですね。
↑マイケルクラスがエレキベアクラスに依存している

エレキベア
エレキベア以外のモンスターはダメということクマね

マイケル
これを解決するにはインターフェイスを使用します。
下記のようにIMonster型で定義し直すと、IMonsterをimplementしているクラスであればどれでも所持モンスターとして設定できるようになります。
下記のようにIMonster型で定義し直すと、IMonsterをimplementしているクラスであればどれでも所持モンスターとして設定できるようになります。
↑インターフェイスで定義しなおした状態

マイケル
これをUML図で表すとこんな感じ!
どのクラスもインターフェイスに依存しており、向きが逆転していることが分かります!
どのクラスもインターフェイスに依存しており、向きが逆転していることが分かります!
↑矢印の向きが逆転している!

エレキベア
浮気クマか・・・

ゴロヤン
ゴロ〜〜〜(モンスター呼ばわりは心外です)

マイケル
これが冒頭で話した 抽象に依存している状態になります!
設定する側はこんな感じです!
設定する側はこんな感じです!
↑注入している例

マイケル
依存性(オブジェクト)を引き渡していますが、
このことを依存性の注入(DI)と言います。
このことを依存性の注入(DI)と言います。

エレキベア
注入する依存性というのは渡すオブジェクトへの依存のことだったクマね

マイケル
ただ見ても分かる通り、今度は注入するクラス(MichaelManager)の方が
各モンスタークラスに依存するようになっています。
これを解決するために、依存関係を定義するクラスをDIコンテナという形で分けることで
DIを実現しやすくする、というわけです。
各モンスタークラスに依存するようになっています。
これを解決するために、依存関係を定義するクラスをDIコンテナという形で分けることで
DIを実現しやすくする、というわけです。
↑DIコンテナの例(VContainerでの実装。IMonsterとElekiBearを紐づけている)

エレキベア
なるほどクマ
DIコンテナは依存性の注入(DI)をしやすくするためのものだったクマね
DIコンテナは依存性の注入(DI)をしやすくするためのものだったクマね

マイケル
その通り!
各用語の意味をまとめると下記のようになります!
各用語の意味をまとめると下記のようになります!
- DIP(依存性逆転の原則)
依存の向きを逆転させて抽象に依存すべきである、という原則。 - DI(依存性の注入)
外側からオブジェクトの参照を渡すことで依存関係を取り除く手法。 - DIコンテナ
依存関係を別途定義するコンテナを作成し、DIを行いやすくするもの。

エレキベア
用語がややこしいクマが、なんとなくイメージは分かったクマね

マイケル
DIやDIコンテナのイメージが湧いてきたところで、
VContainerの使い方について見ていこう!
VContainerの使い方について見ていこう!
VContainerの使い方

マイケル
VContainerは下記の特徴を持つUnityのDIフレームワークです!
- Unityに最適化されたDIフレームワーク
- MonoBehaviourを制御することを思想としている
- 類似ライブラリのZenjectと比べてシンプルで高速

マイケル
DIについてやZenjectとの比較については、
下記にまとまっているのでご参照ください!
下記にまとまっているのでご参照ください!
Comparing to Zenject | VContainer

エレキベア
リファレンスも分かりやすいクマ〜〜〜
導入方法

マイケル
導入はunitypackageかUPM経由でインストールしましょう!
基本の使い方

マイケル
まずはDIコンテナとなるLifetimeScopeを定義します。
ここでは処理の起点となるエントリーポイントとシングルトンのクラスを登録してみます。
ここでは処理の起点となるエントリーポイントとシングルトンのクラスを登録してみます。
↑エントリーポイントとシングルトンを登録

マイケル
それぞれの処理は下記の通り!
コンストラクタに[Inject]を指定することで登録したクラスが渡されるようになります!
コンストラクタに[Inject]を指定することで登録したクラスが渡されるようになります!
↑エントリーポイント
↑シングルトン

マイケル
VContainerに用意されているIStartable、ITickableといったライフサイクルのインターフェイスを使用することで、MonoBehaviourと同様の挙動を行うことができます。
GameManagerクラスを見て分かる通り、何にも依存していないクリーンなコードになっていることが分かります。
GameManagerクラスを見て分かる通り、何にも依存していないクリーンなコードになっていることが分かります。

エレキベア
MonoBehaviour以外を起点にすることができるクマね

マイケル
次はインタフェースと紐づけて登録してみます。

マイケル
こうすることで下記のようにコンストラクタに注入されます。

エレキベア
最初に見ていたDIの形クマね

マイケル
あとはLifetimeScopeクラスをオブジェクトにアタッチして実行すれば、
正常に実行できることが確認できます。
正常に実行できることが確認できます。

エレキベア
シンプルに使えるクマね
MVPの実装

マイケル
次は簡単なMVPの実装をしてみます。
MonoBehaviourクラスはRegisterComponent関数で登録します。
また、エントリーポイントは複数登録することもできます。
MonoBehaviourクラスはRegisterComponent関数で登録します。
また、エントリーポイントは複数登録することもできます。
↑ViewとPresenterの登録

マイケル
それぞれの実装は下記の通り!
PresenterがViewクラスを受け取る形になっていることが確認できます。
PresenterがViewクラスを受け取る形になっていることが確認できます。

マイケル
処理を実行すると、正常に表示されることが確認できました!

エレキベア
MVP実装もスマートに実装できるクマね〜〜
その他の使い方

マイケル
基本的な使い方は以上になりますが、
それ以外の機能や使い方についてもいくつか紹介します!
それ以外の機能や使い方についてもいくつか紹介します!
Injectの方法

マイケル
先ほどはコンストラクタにInjectしましたが、
関数、変数に対してもInjectすることができます。
関数、変数に対してもInjectすることができます。

マイケル
ただし、下記のような理由からコンストラクタ経由でのInjectが推奨されています。
- インスタンス化された時点で依存オブジェクトが揃っていることが保証される
- 依存関係の見通しがよくなり、責務の把握がしやすくなる

マイケル
そのため、MonoBehaviourクラス以外は
基本的にコンストラクタでInjectする方がよいと思います。
基本的にコンストラクタでInjectする方がよいと思います。

エレキベア
責務が把握できてるとクラス分割の目安にもなるクマね〜〜
LifetimeScopeでの登録方法

マイケル
LifetimeScopeでの登録方法には他にもいくつかあります。

マイケル
詳細は公式のリファレンスをご参照ください!
Lifetime Overview | VContainer

エレキベア
オブジェクトのスコープ定義やインスタンス登録もできるクマね
LifetimeScopeの親子階層定義

マイケル
また、LifetimeScopeは階層構造を定義することもできます。
LifetimeScopeオブジェクトのParentに設定することで親に指定できます。
LifetimeScopeオブジェクトのParentに設定することで親に指定できます。

マイケル
また、VContainerの設定ファイルを作成して Root Lifetime Scope に指定することで、
全てのLifetimeScopeの親に指定することもできます。
全てのLifetimeScopeの親に指定することもできます。

エレキベア
階層構造を定義することで
依存関係を更に整理することができるクマね
依存関係を更に整理することができるクマね
Container API

マイケル
最後はCotaninerAPIの使い方についてです!
IObjectResolver型で受け取ることでコンテナに直接アクセスすることができます。
IObjectResolver型で受け取ることでコンテナに直接アクセスすることができます。

マイケル
使い方は下記のようになります。
コンテナ経由でInstantiateすることで動的に生成したオブジェクトにInjectしたり、サービスロケータ的な使い方をすることもできます。
コンテナ経由でInstantiateすることで動的に生成したオブジェクトにInjectしたり、サービスロケータ的な使い方をすることもできます。

エレキベア
これで動的なオブジェクト生成も安心クマね
おわりに

マイケル
というわけで今回はDIコンテナの概要とVContainerの使い方についてでした!
どうだったかな??
どうだったかな??

エレキベア
DIコンテナと聞くと難しそうだったクマが、
シンプルで使いやすかったクマね
シンプルで使いやすかったクマね

マイケル
使い方は簡単だけど、うやむやに依存関係を定義すると逆に複雑になってしまうから
なるべくシンプルな依存関係になるよう整理しながら使用するように気をつけよう!
なるべくシンプルな依存関係になるよう整理しながら使用するように気をつけよう!

マイケル
それでは今日はこの辺で!
アデューー!!
アデューー!!

エレキベア
クマ〜〜〜
【Unity】DIコンテナの概要とVContainerの使い方について 〜完〜