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


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

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

[code]

        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();
        }
    
[/code]

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

※1，※2のどちらの場合でも`PersonManager`は`IPerson`にのみ依存し，`Test1`，`Test2`はどちらも`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コンテナにオブジェクトを生成させる**

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

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

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

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

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

参考

  * [Dependency Injection By Hand · ninject/Ninject Wiki](https://github.com/ninject/Ninject/wiki/Dependency-Injection-By-Hand)
  * [依存性の注入 - Wikipedia](https://ja.wikipedia.org/wiki/%E4%BE%9D%E5%AD%98%E6%80%A7%E3%81%AE%E6%B3%A8%E5%85%A5)
  * [PHPメンターズ -> DIとサービスロケータの違い](http://phpmentors.jp/post/100741112503/di-service-locator)




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

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

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

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

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

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

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

参考

  * [Dependency Injection By Hand · ninject/Ninject Wiki](https://github.com/ninject/Ninject/wiki/Dependency-Injection-By-Hand)
  * [依存性の注入 - Wikipedia](https://ja.wikipedia.org/wiki/%E4%BE%9D%E5%AD%98%E6%80%A7%E3%81%AE%E6%B3%A8%E5%85%A5)
  * [PHPメンターズ -> DIとサービスロケータの違い](http://phpmentors.jp/post/100741112503/di-service-locator)

