DIコンテナを使うメリットが分からない

投稿者: Anonymous

現在C#とNinjectを使ったDIについて学習を進めております.

コンストラクタ・Setter・メソッドの引数の型にインターフェイスを使う方法(※1)と比較して,DIコンテナを使う方法(※2)が優れている点がわかりません.

    interface IPerson { string Name(); }
    class Sol : IPerson { string Name() { return "Sol"; } }
    class Ky : IPerson { string Name() { return "Ky"; } }

    class PersonManager
    {
        private IPerson _p;
        <Inject> public PersonManager(IPerson p) { _p = p; }
        public string SayMyName() { return _p.Name(); }
    }

    // ※1
    void Test1()
    {
        var sol = new PersonManager(new Sol());
        sol.SayMyName();
    }

    // ※2
    void Test2()
    {
        var kernel = new StandardKernel();
        kernel.Bind<IPerson>().To<Sol>();
        kernel.Bind<PersonManager>().ToSelf();

        var sol = kernel.Get<PersonManager>();
        sol.SayMyName();
    }

以下の記事を参考にコードを書きました.
参考:taediumの日記 Ninjectを使ってみた http://d.hatena.ne.jp/taedium/20100307/p1

※1,※2のどちらの場合でもPersonManagerIPersonにのみ依存し,Test1Test2はどちらもSolに依存しているので,依存関係に関しては※1,※2どちらが優れているということは無いと考えています.

※2は※1よりコードが複雑になっていますし,どういう場面でDIコンテナを使用するといいのかがわかりません.

DIコンテナを使用するとこういう場面で非常に役に立つ,という例を教えてください.
C#以外の言語,Ninject以外のDIコンテナでも構いません.

以下追記 2015.06.30

皆さんありがとうございます.

特に@thubさん, @unaristさんのコメントで理解が深まりました.

端的に言えばnew Sol(new Hoge(new Foo()), MAGIC_NUMBER, new Bar())のように生成が複雑になった場合,DIコンテナを使ったほうが便利.ということだと理解しました.

また,DIコンテナ側の機能で,オブジェクトのスコープをSingletonやThreadLocalにできたり,Privateなフィールド,メソッドにDIできるというのは確かなメリットだと感じました.

追記の追記(読み飛ばして結構です) 2015.07.01

私の質問の仕方が良くなかったのですが,この質問はDI自体のメリットを聞くつもりではなく,DIのコードを自分で書く場合と,DIコンテナを使った場合の差が知りたい.という意図で質問をしたつもりでした.InterfaceによるDIは日常的に行っており,そのメリットも理解・体感しております.

DIのコードを自分で書く場合も,DIコンテナを使う場合でも,オブジェクトの生成ルールを書く際は具象クラスに依存する.DIコンテナのオブジェクト生成ルールを書く場合も,自分でFactoryメソッドを用意する場合と比較してコード量も減るようには見えない.どこにメリットがあるんだろう?という疑問からの質問でした.

みなさんのコメントのおかげでとても理解が深まりました.ありがとうございました.

解決

歴史には沿っていないと思いますが、次のように考えてみました。

  • 巨大なプログラムがある
  • それをコンポーネントに切り分けていく。けれど替えがきかない。
  • インターフェイスを挟む。替えはきくが、内部を変更する必要がある。
  • 依存性を注入する。その代りオブジェクト生成が複雑になり、その生成ロジックが分散している。
  • あらかじめ登録しておいた生成ルールを使い、DIコンテナにオブジェクトを生成させる

依存性の注入までは小規模のコードでもわかりやすく、実際テストに便利だったりします。一方で、それぞれのオブジェクトへの依存が少ない、質問に例示されたようなコードでは「同じ生成ロジックが分散する」と感じにくいですし、コンテナを使うメリットは薄いかもしれません。

しかし IPersonPersonManager を生成しなければならない個所が増えた場合はどうでしょうか。個別に new したとして、それを変更したくなったら?単に具象クラスが違うということ以外にも、生成方法が異なることがあります。あるいは既存のインスタンスを使いたいかもしれません。

DIコンテナを使えば、「これはこうすれば生成できる」というパターンを最初に登録しておくだけで、あとはコンテナが適切に組み合わせてくれます。Aが依存するBはさらにCに依存していて・・・みたいなのも含め。

また、どのような場合でも kernel.Get<T>() という一貫した手順でオブジェクトを生成できるというのは、コードの可読性にもつながるのではないでしょうか。

なお、DI は外部からオブジェクトを注入しますが、直接具象クラスに依存することを避けるだけならファクトリパターンやサービスロケータといった手法もあります。

参考

回答者: Anonymous

Leave a Reply

Your email address will not be published. Required fields are marked *