ZenjectでDIの真理を悟る

現在グレンジでUnityを用いたゲーム開発を行っているみくりやと申します。
[グレンジ Advent Calendar 2017](https://qiita.com/advent-calendar/2017/grenge) 3日目の記事になります。
Unityについてのホットな話題を探していたところ **Zenject** にたどり着き、面白そうだったので
少し触ってみようと思います。
→*[Zenject Dependency Injection IOC](https://www.assetstore.unity3d.com/jp/#!/content/17758)* (AssetStore)

#Zenjectとは
一言で言うと「 **Unityを対象としたDIフレームワーク** 」となります。そして無料（MITライセンス）。
ポケモンGOでも採用されているようです。
詳しい説明はgithubのREADMEを読めばわかりやすいかもしれません。
→*[github](https://github.com/modesttree/Zenject)*

#DIとは
Dependency Injection。しばしば日本語で「 **依存性の注入** 」と訳されます。
正直DIという単語自体「入社直後の研修でやったな」くらいの記憶しかなく、依存性の注入というワードのわかりにくさもあいまってあっちいけと敬遠したくなります。
改めて調べてみましたが、簡単に言うと「 **部品同士を疎結合化し依存性を無くす** 」ということみたいです。疎結合化って言われると途端にわかりやすく感じます。
その結果メンテしやすくなったり再利用しやすくなったりテストしやすくなったりするようですね。
なんだか良いやつっぽいです。君とは仲良くなれそうだ。

#触ってみる
##簡単な例
まずDIパターンでない場合。

```
public class ClassA : MonoBehaviour {
	private ClassB _classB;

	void Start () {
		_classB = new ClassB ();
		_classB.CreateLog ();
	}
}

public class ClassB : MonoBehaviour {
	public void CreateLog () {
		Debug.Log ("hoge");
	}
}
```
このときClassAはClassBをnewしなければ動作できないのでClassAはClassBに依存している状態となります。
この例をZenjectを用いて疎結合化します。

SceneContextオブジェクトの生成
<img width="317" alt="9b63c51b-62ff-6af1-c1dd-6883271429db.png" src="https://qiita-image-store.s3.amazonaws.com/0/20374/2357656a-72b1-595e-3693-a8b1aa3b3635.png">

Installerスクリプトの生成
<img width="379" alt="2.png" src="https://qiita-image-store.s3.amazonaws.com/0/20374/3a5d44f1-af2a-c449-308f-d6356c6dd5ed.png">


SceneContextのInstallerプロパティにInstallerを追加
<img width="504" alt="3.png" src="https://qiita-image-store.s3.amazonaws.com/0/20374/de5eb16d-0ffa-0ef4-c2f7-f23cd214923d.png">

Installerスクリプトには以下を記述します。

```
public class TestInstaller : MonoInstaller<TestInstaller>
{
	public override void InstallBindings() {
		Container.Bind<ClassA>().AsSingle();
	}
}
```

ClassAを以下のように修正します。

```
public class ClassA : MonoBehaviour {
	[Inject]
	private ClassB _classB;

	void Start () {
		_classB.CreateLog ();
	}
}
```

ClassBオブジェクトにZenjectBindingをAddComponentしComponentsプロパティにClassBを追加します
<img width="504" alt="4.png" src="https://qiita-image-store.s3.amazonaws.com/0/20374/f0b88a94-4a3c-6a57-8305-8682ebfb9508.png">


するとZenjectがごにょごにょしてくれてClassBをnewしなくてもログを吐き出すことができるようになります。
<img width="373" alt="5.png" src="https://qiita-image-store.s3.amazonaws.com/0/20374/7aa837f3-d112-1a6a-9581-d3d0338d9290.png">


##Singletonで書いてたところに使ってみる
以下のようなシングルトンのGameManagerを

```
public class GameManager : Singleton<GameManager> {

	public int hoge0;

	public int hoge1;

	public int hoge2;
}

```

単なるMonoBehaviour継承クラスにしてしまい

```
public class GameManager : MonoBehaviour {

	public int hoge0;

	public int hoge1;

	public int hoge2;
}
```
ZenjectBindingをAddComponentします。
<img width="274" alt="6.png" src="https://qiita-image-store.s3.amazonaws.com/0/20374/74e17479-45d5-d228-a510-6e166beb176c.png">

先ほど作成したClassAを改変します。

```
public class ClassA : MonoBehaviour {
	[Inject]
	private ClassB _classB;
	[Inject]
	private GameManager _manager;

	void Start () {
		_classB.CreateLog ();
		Debug.Log (_manager.hoge0 + " " + _manager.hoge1 + " " + _manager.hoge2);
	}
}
```
Installerは改変しなくて大丈夫です。
すると
<img width="447" alt="7.png" src="https://qiita-image-store.s3.amazonaws.com/0/20374/8932ce06-d71e-199e-60ac-505b5e49cc17.png">


##Installerをprefab化してみる 
InstallerのprefabをSceneContextに追加できるのでシーン上に配置しなくても使用することができ、シーンをキレイにすることができます。
PrefabInstallersに追加するだけ。
<img width="274" alt="8.png" src="https://qiita-image-store.s3.amazonaws.com/0/20374/9e175efb-0924-295d-0e4b-656c48b5b9e4.png">

##ScriptableObjectで使ってみる
ScriptableObjectを扱うことも可能です。

SciptableObjectInstallerを作成
<img width="383" alt="9.png" src="https://qiita-image-store.s3.amazonaws.com/0/20374/9dfb9f77-89a1-8c07-0bc5-aafaac34884a.png">


SciptableObjectInstallerが生成されます。
<img width="229" alt="10.png" src="https://qiita-image-store.s3.amazonaws.com/0/20374/9c97604c-5ae1-5444-4929-c45746f6ebd6.png">


SciptableObjectInstallerを編集

```
[CreateAssetMenu(fileName = "SettingInstaller", menuName = "Installers/SettingInstaller")]
public class SettingInstaller : ScriptableObjectInstaller<SettingInstaller> {
	public Setting setting;

	public override void InstallBindings () {
		Container.BindInstances(setting);
	}
}

[Serializable]
public class Setting {
	public int hoge3;
}
```

ProjectのメニューにInstallers/SettingInstallerが増えているのでそこからScriptableObjectをAssetsファイルとして出力
<img width="324" alt="11.png" src="https://qiita-image-store.s3.amazonaws.com/0/20374/70125cf4-31ff-2e8e-f866-cdf4a5d735ec.png">


適当な値を入れます。
<img width="273" alt="12.png" src="https://qiita-image-store.s3.amazonaws.com/0/20374/5470230b-130b-aa59-a9ee-74bef19f4a56.png">

SceneContextに追加
<img width="275" alt="13.png" src="https://qiita-image-store.s3.amazonaws.com/0/20374/c855949d-6c67-d122-cfef-949525637136.png">



ClassAを改変します。

```
public class ClassA : MonoBehaviour {
	[Inject]
	private ClassB _classB;
	[Inject]
	private GameManager _manager;
	[Inject]
	private Setting _setting;

	void Start () {
		_classB.CreateLog ();
		Debug.Log (_manager.hoge0 + " " + _manager.hoge1 + " " + _manager.hoge2);
		Debug.Log (_setting.hoge3);
	}
}
```

すると
<img width="360" alt="14.png" src="https://qiita-image-store.s3.amazonaws.com/0/20374/2de6e0f6-5e13-25cd-ff5c-197a2c4365d1.png">



#まとめ
Unityでルールを決めず自由にコードを書いてしまうとしっちゃかめっちゃか
してしまいがちなので今回とりあげたZenjectのようなフレームワークは闇を生まないためにも有効な手段だと感じました。Zenjectを使うとかなりシンプルにまとめあげることができそうな気がします。
まだまだ簡単なことしかできていないので引き続きZenjectを触ってDIの真理へと近づいて行きたいと思います。