Extenject (+ MV(R)P) に触れてみた

Extenject とは

Unity用のDIフレームワークという認識.

DIって何?

よく言われているDependency Injection.

DIの説明としては,以下の記事に詳しく書かれている.

【Unity】Zenjectに学ぶDependency Injection - Qiita

詳細を外部から渡すことで疎結合にしていくアプローチがDI.
しかし,いくら抽象化して疎結合して行っても,最終的には必ず誰かが依存関係を知ることになる.
DIパターンを実装する場合,依存関係を知っている専門のクラスを用意して,そのクラスがインスタンスを渡すという
アプローチを取る.この役割をDIコンテナと呼ぶ.

Extenjectの機能・特徴

Extenject(旧Zenject)はDIを行うための機能を持つだけでなく,下記の機能も持つ.

  • MonoBehaviourを継承せずにUpdateを行う機能
  • Pub/Sub
  • Object Pool

また,特定のソフトウェアアーキテクチャに依存しない特徴を持つ.
MVC,MV(R)P,MVVM,Clean Architectureなどなど.

Extenjectを軽く触れてみる

こちらの記事のサンプルを利用しました.

https://matcha-choco010.net/2019/08/24/unity-mvrp-zenject-multiscene/

Extenject触れる前はコメントアウトしている.

MVPの作り方は下記記事詳細.

xrdnk.hateblo.jp

f:id:xrdnk:20200314160359p:plain

(はじめてのUniRx from torisoupさんより抜粋)

Model

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UniRx;

public class TestHPModel //: MonoBehaviour 
{
    private ReactiveProperty<int> _hp = new ReactiveProperty<int>(100);
    public IReadOnlyReactiveProperty<int> HP => _hp;

    public void Damage(int damage)
    {
        var tmp = _hp.Value - damage;
        _hp.Value = (tmp < 0) ? 0 : tmp;
    }
}

ModelはMonoBehaviourを継承する必要がなくなり,適当な空GameObjectにアタッチしなくてもよくなった.

View

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using TMPro;
using UniRx;
using UnityEngine.UI;

public class TestHPView : MonoBehaviour
{
    [SerializeField] private TextMeshProUGUI text;
    [SerializeField] private Button button;

    public IObservable<Unit> OnDamage => button.OnClickAsObservable();

    public void DisplayHP(int hp)
    {
        text.text = hp.ToString();
    }
}

Viewは変わらず.

Presenter

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UniRx;
using Zenject;

public class TestPresenter //: MonoBehaviour 
{
    // //Extenject導入前
    // public TestHPView TestHPView;
    // public TestHPModel TestHPModel;

    // void Start()
    // {
    //     // Model -> View
    //     TestHPModel.HP.Subscribe(TestHPView.DisplayHP);

    //     // View -> Model
    //     TestHPView.OnDamage.Subscribe(_ => TestHPModel.Damage(20));
    // }

    // Extenject導入後
    public TestPresenter(TestHPView testHPView, TestHPModel testHPModel)
    {
        // Model -> View
        testHPModel.HP.Subscribe(testHPView.DisplayHP);

        // View -> Model
        testHPView.OnDamage.Subscribe(_ => testHPModel.Damage(20));
    }
}

Presenterは劇的に変わった.素晴らしい.
MonoBehaviourの継承がなくなってアタッチする必要なくなったし,
ViewとModelをインスペクタで逐一D&Dとかする必要がなくなった.
StartではなくコンストラクタでViewとModelを受け取り(典型的なDIパターン),処理をしている.

f:id:xrdnk:20200319233330g:plain

終わりに

こちらの「ZenjectチョットワカルBook」の2章まで読んだ.

ZenjectチョットワカルBook - EFB~相手は死ぬ~ - BOOTH

DI自体の概念は業務でバリバリSpringフレームワークみたいなので触れたり,
Factoryを作ってたりしていたので,すぐに理解できた.

Factory Methodパターン参照.

xrdnk.hateblo.jp

後はExtenject特有のBindの記述式とかやり方とかに慣れていこうと思います.

上のZenjectチョットワカルBookは上巻らしい.
下巻出るまでに完全に理解したい.

設計系とかUniRxとか楽しいけど沼を感じる.
ちゃんと理解できるところまでどこまでいけるだろうか….ガンバロウ.