LoginSignup
9
6

More than 5 years have passed since last update.

Zenjectを導入する際に考えておくこと

Last updated at Posted at 2018-07-31

Zenjectというよりは、DIコンテナを使用する設計パターンを導入する時に検討しなければならないこと全般に当てはまるかと思います。実際にプロジェクトでZenjectを使用し、盛大にアンチパターンを踏んでいたのでその戒めを込めて。

Zenjectとは

Unityでの使用に特化したDIコンテナです
https://github.com/svermeulen/Zenject

DI/DIコンテナって何?

自分が説明するよりググったほうが有益な情報が多いです。
https://ja.wikipedia.org/wiki/依存性の注入
DI・DIコンテナ、ちゃんと理解出来てる・・?

DIコンテナを使うと何が良いの?

まず、DI/DIコンテナを使うメリットについては山程記事があるので、調べてみましょう。
一つのメリットとしては、SOLIDの原則の一つでDの部分、
DIP:The Dependency Inversion Principle 依存関係逆転の原則
守るための手助けをしてくれます。
クラスを生成する際に、そのクラスが依存するクラスのインスタンスを渡す手間を省いてくれるものです。

詳細に説明するよりも、これも沢山記事があるので読んであさってみると良いと思います。
(図とかコードとか書くのが面倒なだけ...)

Zenject導入の前に

多人数のチームで導入するには、上記のことをチーム内で共有・勉強するようにしましょう。
なんとなく最近Zenjectっていうものが便利だから導入しようぜ!だと、
ほぼ100%の確率でDIコンテナを使用する際のアンチパターンを踏みます(踏みました)
Zenjectのことを調べるよりも、DIコンテナを使用する設計パターンをチーム内で共有されているというのが大事です。

よくやってしまうのが、動的にオブジェクトを生成する際、そのオブジェクトに[Inject]Attributeが有り、
そのオブジェクトを生成する際にサービスロケーターパターンに陥ることです。
サービスロケーターが本当にアンチパターンなのかは置いておいて、出来るだけ避けようとは言われています。

[Inject]Attribut が設定されているオブジェクトを動的に生成する際には、Factoryを使うのがセオリー

ZenjectのFactoryのドキュメントでもしっかりと言及されています
https://github.com/svermeulen/Zenject/blob/master/Documentation/Factories.md#theory

However, the above code is an example of an anti-pattern. This will work, and you can use the container to get access to all other classes in your app, however if you do this you will not really be taking advantage of the power of dependency injection. This is known, by the way, as Service Locator Pattern.

ただし、上記のコードはアンチパターンの例です。これはうまく動作し、コンテナを使用してアプリケーションの他のすべてのクラスにアクセスできますが、これを行うと依存関係注入の力を実際に利用することはできません。これは、サービスロケータパターンとして知られています。

ただ、[Inject]属性を安易に使うと、Factoryで生成するオブジェクトの中でさらに、Factoryによって生成する必要があるオブジェクトが存在したりして、
Factory -> Factory -> Factoryのようなネストした構成になることもあります。
この場合は、依存関係自体がどっかおかしいので設計を見直したほうが良いです。

また、サービスロケーターはアンチパターンなのか?というのは置いておいて、
クリーンアーキテクチャや、DDD的な思想での多層設計となっている場合、とある層でのみサービスロケーターとするというのは、個人的には有りな気はしています。Factoryを作るよりも、その層内でDIコンテナを隠蔽してしまうほうがすっきりする場合もあるのでは?

何でもかんでもDIコンテナで依存関係の解決をしない

Zenjectは使い慣れると本当に便利です。そして色々なことが出来すぎます。
その上で気をつけないといけないのはDIコンテナの乱用です。
本当にその依存関係はDIコンテナを使用しなければならないのか?というのをじっくり考えたほうが良いかと思います。
Component同士で依存関係があり、且つシーン上に配置されているのであれば、[SerializeField] でポチッと参照を入れてあげるだけで済みますしね。

[SerializeField]publicフィールドで、オブジェクトの参照を突っ込むのは、依存性の逆転 という意味で考えると非常に有効なようにも思えます。

ただし、[SerializeField] では設定できないインターフェースを[Inject]だと指定できるというのは、Zenjectの大きい利点でもあります。

脳死で、DIコンテナに突っ込むのではなく、その前に一呼吸置いて考えてみましょう

具象クラスをBindしない

[Inject] では基本的には インターフェースを指定しましょう。
インターフェースを実装した具象クラスそのもの渡すようでは、Singletonパターンと大差はないです。
インターフェースにすることで、テスト時にモッククラスと置き換えやすくなるのです。

ただ、プロトタイプの作成や、ゲームジャムとかで使うとかであれば有りだと思います。
使用方法を熟知しているのであればSingleton使うよりもサクッと依存関係の解決できますしね。

[Inject] がたくさんあるクラスが出来たら...

クラス分割などを検討しましょう。 [Inject]Attributeが設定されているフィールドが山のように出てくると、
そのクラスは他のクラスに依存しすぎです。クラス分割などをしつつ設計を見直しましょう

よくわかんないし、とりあえずZenject使ってみたいんだけど...

使ってみると良いじゃないかなと!
アンチパターンやダメな設計は踏んでみないことには実感がわかないものです。
Singleton絶対禁止などの縛りを入れてみて、どうやったらこれは解決できるのか?など手を動かしてみないことには身につかないですしね。

9
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
6