【Unity】Zenjectをちょっと触ってみた

#はじめに
Zenjectがイイらしいという情報を見かけたので軽く触ってみました。 

#環境
Unityのバージョンは5.3.5f1, OSはWindows10です

#Zenject？
https://github.com/modesttree/zenject

>Zenject is a lightweight dependency injection framework built specifically to target Unity 3D

DI (Dependency Injection, 依存性の注入) を扱うためのフレームワークだそうです。

#1. とりあえず使ってみる

##1. Zenjectのダウンロード
以下URLからダウンロードしてUnityProjectに入れます。
https://github.com/modesttree/Zenject/releases

<a href="https://www.assetstore.unity3d.com/jp/#!/content/17758">AssetStore</a>からもダウンロードできます
<br><br>

筆者は Zenject-WithSampleGames.v4.5.unitypackage をダウンロードしました。

##2. Hello World
<a href ="https://github.com/modesttree/zenject#hello-world-example">公式のHelloWorld</a>を参考に実際に動かしていきます。

1. Hierarchyで右クリックして、Zenject -> Scene Context を選択して、SceneContextを作成
![image](https://qiita-image-store.s3.amazonaws.com/0/44288/66b0c3fc-6b95-b654-0ab9-92f685469e14.png)

<br>
2. 以下のスクリプトを作成して好きなGameObjectにアタッチ。

```TestInstaller.cs
using Zenject;
using UnityEngine;
using System.Collections;

public class TestInstaller : MonoInstaller
{
    public override void InstallBindings()
    {
        Container.BindInstance("Hello World!");
        Container.Bind<TestRunner>().NonLazy();
    }
}

public class TestRunner
{
    public TestRunner(string message)
    {
        Debug.Log(message);
    }
}
```

3.SceneContextにTestInstaller付きのオブジェクトをアタッチ
![image](https://qiita-image-store.s3.amazonaws.com/0/44288/2af21ca6-d2e1-607c-f02b-3f5e552bad2c.png)


4.Validate ( Ctrl + Shift + V )するとConsoleに以下のようなメッセージが表示されます.
![image](https://qiita-image-store.s3.amazonaws.com/0/44288/9f6f3643-ba21-2687-0992-50d78742d859.png)


あまり理解できていないのですが、InstallBindings()内にてクラス間の依存関係を定義しているみたいです。

#2. サンプルをちょっと覗いてみた
同梱のサンプルゲームに使われているコードがどのような意味を持つのか表にまとめてみました。
各コードはInstallBindings()の中で呼んでいるものとしています。
<br>

|コード  |意味  |エラー例|　備考|
|---|---|---|---|
|```Container.Bind<IF>().To<A>()```  |インターフェースIFをクラスAが継承していることを保証する？ |  ```A```が```IF```を継承していないとエラー | IFの代わりにクラスBを入れても動くみたいです 
| ```Container.BindFactory<A, B>()```  |クラス```B```が```Factory<A>```に変換可能であることを保証する？|```B```が```Factory<A>```へ変換できないとエラー|Factory<>はZenjectで提供されているクラス
|```Container.BindInstance<int>(x).AsSingle()```|int型の変数```x```が変わらないことを保証する？|1回目のBindInstance呼び出しと2回目のBindInstance呼び出しで異なるint値が渡されるとエラー
|```Container.BindAllInterfaces<A>().To<B>()```|クラスAが継承している全てのインターフェースをクラスBが継承していることを保証する？| Aが継承するインターフェースの中にをBが継承していないものがあるとエラー| 

#3. Injectを使ってみた
Injectもちょっと触ってみました。 DIを行うためのものらしいです

##引数なしメソッドを呼ぶ
シーンを再生するとA()メソッドが実行されます.

```TestInstaller.cs
using UnityEngine;
using Zenject;

public class TestInstaller : MonoInstaller
{
	public override void InstallBindings()
	{
		Container.Bind<Hoge>().NonLazy();

	}
}

public class Hoge
{
	[Inject]
	void A()
	{
		Debug.Log("A");
	}
}
```

実行結果
![image](https://qiita-image-store.s3.amazonaws.com/0/44288/a85084f4-174a-666f-793b-ed33cd0f24ff.png)

##引数ありメソッドを呼ぶ
WithArgumentsを使うとメソッドに引数を渡せるみたいです。

```TestInstaller.cs
using UnityEngine;
using Zenject;

public class TestInstaller : MonoInstaller
{
	public override void InstallBindings()
	{
		Container.Bind<Hoge>().WithArguments(810, 114514).NonLazy();
	}
}

public class Hoge
{
	[Inject]
	void A(int x, int y)
	{
		Debug.Log("A : x = " + x + ", y = " + y);
	}
}
```

実行結果
![image](https://qiita-image-store.s3.amazonaws.com/0/44288/5c93a93c-b51c-19bc-f303-6b55e2c326d6.png)

#4. ITickableを使ってみた
ITickableを使うことで毎フレーム処理を実行するクラスを作ることができるらしいです。

##実際に使ってみる
シーンを再生するとTick()メソッドが毎フレーム実行されます

```TestInstaller.cs
using UnityEngine;
using Zenject;

public class TestInstaller : MonoInstaller
{
	public override void InstallBindings()
	{
		Container.Bind<ITickable>().To<A>().AsSingle();
	}
}

class A : ITickable
{
	public void Tick()
	{
		Debug.Log("Tick");
	}
}
```

シーンを再生すると以下のようなLogが出力されます。
![image](https://qiita-image-store.s3.amazonaws.com/0/44288/6346adf5-0c2b-30fe-8366-18b3eadbb4b1.png)

#5. IInitializableを使ってみた
開始時にInitialize()メソッドを実行させるためのインターフェースらしいです

## 実際に使ってみる
シーンを再生するとInitialize()メソッドが１度実行されます

```TestInstaller.cs
using UnityEngine;
using Zenject;

public class TestInstaller : MonoInstaller
{
	public override void InstallBindings()
	{
		Container.Bind<IInitializable>().To<A>();
	}
}

class A : IInitializable
{
	public void Initialize()
	{
		Debug.Log("Initialize");
	}
}
```

##IInitializableをInjectで代用する
以下のように書くことでも同様のことができるみたいです

```TestInstaller.cs
using UnityEngine;
using Zenject;

public class TestInstaller : MonoInstaller
{
	public override void InstallBindings()
	{
		Container.Bind<A>().NonLazy();
	}
}

class A
{
	[Inject]
	public void Initialize()
	{
		Debug.Log("Initialize");
	}
}
```

#参考URL
GitHub - modesttree/Zenject
https://github.com/modesttree/zenject

アセットストア - Zenject Dependency Injection IOC
https://www.assetstore.unity3d.com/jp/#!/content/17758