Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Zenjectを導入する前に

いも
December 19, 2019

 Zenjectを導入する前に

2019/12/19 Roppongi.unity #6 のLT資料です

いも

December 19, 2019
Tweet

More Decks by いも

Other Decks in Programming

Transcript

  1. ━━━━━━━━━━━━━━━━━
    Zenjectを導⼊する前に
    いも(@adarapata)
    ━━━━━━━━━━━━━━━━━
    2019/12/19 Roppongi.unity #6 1

    View full-size slide

  2. ⾃⼰紹介
    いも (@adarapata)
    adarapata.com
    仕事でUnity触ってるエンジニア
    テスト、DIとかよく話すおじさん
    2019/12/19 Roppongi.unity #6 2

    View full-size slide

  3. 宣伝
    「ZenjectチョットワカルBook」
    基礎的な使い⽅を纏めた本
    多分⽇本で唯⼀のZenject本
    boothで電⼦版販売中
    後編もうちょっとまって
    2019/12/19 Roppongi.unity #6 3

    View full-size slide

  4. はなすこと
    DIフレームワークを導⼊するその前に考えること
    Zenject内部の話はあんまりしない
    対象者
    すでに動いているプロダクトにZenjectを導⼊しようとしている⼈
    2019/12/19 Roppongi.unity #6 4

    View full-size slide

  5. Zenject is 便利
    シングルトン無くせる!
    疎結合な設計ができる!
    テスタビリティ⾼い!
    わが社もDIフレームワークでイケイケ開発だ!
    2019/12/19 Roppongi.unity #6 5

    View full-size slide

  6. 既存プロダクトにZenjectを⼊れる
    動いてるものにライブラリを⼊れるのドキドキするよね
    ミニマムにスタートしたくなるのが⼼情
    特定のクラスだけDIするとかから始めたい
    2019/12/19 Roppongi.unity #6 6

    View full-size slide

  7. Zenject対応の⼀例 シングルトン依存
    動的に作られるFoo
    public class Foo {
    public void DoFoo()
    {
    var some = BarSingleton.Instance.SomeLogic();
    // some
    でなんやかんや
    }
    }
    テストを書きやすくしたい!
    シングルトン依存を無くしたい!
    2019/12/19 Roppongi.unity #6 7

    View full-size slide

  8. interfaceをBindしてDIだ!
    public class Foo {
    private IBarSingleton _bar;
    public Foo(IBarSingleton bar) {
    _bar = bar;
    }
    public void DoFoo()
    {
    var some = _bar.SomeLogic();
    // some
    でなんやかんや
    }
    }
    Container.Bind().AsCached();
    Container.Bind().To().AsSingle();
    2019/12/19 Roppongi.unity #6 8

    View full-size slide

  9. 動的に作られるならFactoryにしないといけないよね
    public class Foo {
    private BarSingleton _bar;
    public Foo(BarSingleton bar) {
    _bar = bar;
    }
    public void DoFoo()
    {
    var some = _bar.SomeLogic();
    // some
    でなんやかんや
    }
    public class Factory : PlaceholderFactory {}
    }
    Container.BindFactory();
    Container.Bind().To().AsSingle();
    2019/12/19 Roppongi.unity #6 9

    View full-size slide

  10. Fooを⽣成してたところもZenject対応必要だよね
    public class FooSpawner {
    private Foo.Facotry _factory;
    public FooSpawner(Foo.Factory factory) {
    _factory = factory
    }
    public void Spawn() {
    // var foo = new Foo();
    今までこうだった
    var foo = _factory.Create();
    }
    }
    Container.Bind().AsCached(); //
    追加
    Container.BindFactory();
    Container.Bind().AsSingle();
    2019/12/19 Roppongi.unity #6 10

    View full-size slide

  11. FooSpawnerに依存しているクラスもZenject対応必要だよね
    ↑に依存してるやつも必要だよね
    ↑に依存してるやつ(ry
    ↑に依存し(ry
    ↑(ry
    結局シーンのオブジェクト全部対応必要じゃね?
    影響範囲やばい
    1クラスを綺麗にしたかっただけなのに
    2019/12/19 Roppongi.unity #6 11

    View full-size slide

  12. Zenject対応の⼀例 循環参照
    public class Fizz {
    private Buzz _buzz;
    public Fizz(Buzz buzz) {
    _buzz = buzz;
    }
    }
    public class Buzz {
    private Fizz _fizz;
    public Buzz(Fizz fizz) {
    _fizz = fizz;
    }
    }
    Zenjectはコンストラクタインジェクションの循環参照を許さない
    2019/12/19 Roppongi.unity #6 12

    View full-size slide

  13. フィールドインジェクションなどで頑張ることになる
    public class Fizz {
    [Inject]
    private Buzz _buzz;
    }
    public class Buzz {
    [Inject]
    private Fizz _fizz;
    }
    対応はできるがZenject以外の⽅法でこのクラスを扱うことは難しくなる
    2019/12/19 Roppongi.unity #6 13

    View full-size slide

  14. Zenjectを使おうとして明らかになることがある
    ⾃分のコードはどのような依存関係を持っているのか
    それは適切なものなのかが如実に表れる
    循環参照があるとしんどくなる
    1クラスに⼤量の依存があるとしんどくなる
    階層がめっちゃ深くてもしんどくなる
    この部分だけZenjectみたいなつまみ⾷いは割と⼤変
    これらを強引にZenjectの機能で解決しようとすると苦戦を強いられ導⼊コストと
    運⽤コストが跳ね上がる
    2019/12/19 Roppongi.unity #6 14

    View full-size slide

  15. あなたのコードはDIできますか?
    DIフレームワークはコードを綺麗にしてくれない
    ⼈間がDIフレームワークを活⽤してコードを綺麗にする
    DIできる状態じゃないとDIできない禅問答
    Zenjectを使う前に、そもそもZenjectを使えるような作り⽅にしないとい
    けない
    2019/12/19 Roppongi.unity #6 15

    View full-size slide

  16. Zenjectでしか解決できない問題はそんなにない
    スコープを狭めれば⼿動でDIはできる
    public class FooSpawner {
    public void Spawn() {
    var foo = new Foo(BarSingleton.Instance); // Non Zenject
    //var foo = _factory.Create();
    }
    }
    テストを書きやすくしたい! =>
    シングルトン依存を無くしたい! =>
    2019/12/19 Roppongi.unity #6 16

    View full-size slide

  17. インタフェースに依存させれば循環参照を無くせる
    public interface IFizz { }
    public class Fizz : IFizz {
    private Buzz _buzz;
    public Fizz(Buzz buzz) {
    _buzz = buzz;
    }
    }
    public class Buzz {
    private IFizz _fizz;
    public Buzz(IFizz fizz) {
    _fizz = fizz;
    }
    }
    2019/12/19 Roppongi.unity #6 17

    View full-size slide

  18. Zenjectを使う = 設計と向き合うこと
    導⼊しただけでは何も変わらない、導⼊できるかもわからない
    疎結合になるのではなく、疎結合にしていかないといけなくなる
    リファクタリングはほぼ必須
    リファクタリングの際にテストが活きてくる
    2019/12/19 Roppongi.unity #6 18

    View full-size slide

  19. Zenjectを導⼊する前に
    銀の弾丸ではないことを理解する
    DIパターンを適⽤できるコードなのか⾒直す
    できない状態であるなら、今後どういうアプローチを取るのか考える
    地道なリファクタリングしましょう
    Zenjectに依存しないこと
    2019/12/19 Roppongi.unity #6 19

    View full-size slide

  20. Zenjectに依存とは?
    コードレベルで⾒ると依存は避けられない(assembly的な意味で)
    ではなく、設計レベルで解決しなければならないものをZenjectを使って解決す
    ることを Zenjectに依存する と考えている
    循環参照をフィールドインジェクションでねじ伏せる
    ProjectContextでグローバルなBindを⼤量展開
    単⼀責任原則を⼤きく違反していてもInstallerで⼿軽に⼤量Bindできる
    ようにする etc..
    鎮痛剤としては使えないことを理解する
    2019/12/19 Roppongi.unity #6 20

    View full-size slide

  21. それでもZenjectはいいぞ
    Zenjectを⼊れずとも運⽤するアプリなら設計とは向き合わなければならない
    そこでDIという選択肢が増えるのはよいこと
    導⼊時のコストは未来への先⾏投資
    OSSなので集合知がある。あなたの悩みは誰かが解決してる
    誰かの悩みをあなたが解決できる
    巨⼈の肩に乗っかろう
    2019/12/19 Roppongi.unity #6 21

    View full-size slide