単純なDIコードではなくIoCコンテナーが必要なのはなぜですか?[閉まっている]


598

私はしばらくの間、依存性注入(DI)を使用しており、コンストラクター、プロパティ、またはメソッドのいずれかに注入しています。制御の反転(IoC)コンテナーを使用する必要性を感じたことはありません。ただし、読むほど、コミュニティからIoCコンテナを使用するようにというプレッシャーが強くなります。

StructureMapNInjectUnityFunqなどの.NETコンテナで遊んだ。IoCコンテナーがコードにどのように利益をもたらすか、または改善するかはまだわかりません。

また、同僚の多くが理解できないコードを目にするため、職場でコンテナを使い始めるのも怖いです。彼らの多くは新しい技術を学ぶことに消極的かもしれません。

IoCコンテナを使用する必要があることを納得してください。職場で他の開発者と話すとき、これらの引数を使用します。


6
良い質問。私はIOCのアイデアに非常に新しいので、コメントでこれに答えています。プラグアンドプレイコンポーネントと疎結合のアイデアのように見えます。現在のコンポーネントの代わりに他のコンポーネントを使用する必要があるかどうかは、ニーズに基づいています。IOCの使用は、IMOが発生した場合に、そのような変更に備えてコードを準備することです。
shahkalpesh、2009年

3
@shahkalpeshですが、単純なDIで疎結合を実現できます。しかし、私が構成ファイルを使用する場合には、私はあなたの要点を理解しています。ただし、構成ファイルを使用するかどうかはわかりません。設定ファイルは非常に冗長で、リファクタリングが困難で、複数のファイルを切り替えることができます。
ヴァディム

110
主にIoCを使用する理由を探しているようなので、コメントを追加するだけです。しかし、あなたの問題について他に何か言う必要があります。チームのより悪い人のレベルに、または彼らが学びたくないと恐れて、コードを書くことはできません。あなたはプロであるだけでなく、成長する将来にも責任があり、チームはあなたを引き留めるべきではありません。
ケビンシェフィールド

4
@Kevin、実際にはIoCを使用しています。みんなにIoCを教えるのはそれほど難しいことではありませんでした。
Vadim

21
なぜモデレーターが非常に賛成され、有益な質問を閉じるのか、私にはわかりません。おそらくこの場合、ウィルは質問を理解していないのでしょうか、それとも人々が何のためにstackexchangeを使用するのか理解していないのでしょうか?「stackexchangeのQ&A形式に適していない」場合は、ポリシーが間違っていると考えてください。また、何百もの有用な質問があり、何百もの賛成票と賛成票が有効であるとは限りません。
NickG

回答:


441

うわー、ジョエルがこれを支持するとは信じられない:

var svc = new ShippingService(new ProductLocator(), 
   new PricingService(), new InventoryService(), 
   new TrackingRepository(new ConfigProvider()), 
   new Logger(new EmailLogger(new ConfigProvider())));

これ以上:

var svc = IoC.Resolve<IShippingService>();

多くの人々は、依存関係の連鎖が入れ子になる可能性があることに気づいておらず、手動でそれらを結び付けるのは手に負えなくなります。ファクトリがあっても、コードの複製は価値がありません。

はい、IoCコンテナは複雑になる可能性があります。しかし、この単純なケースでは、信じられないほど簡単であることを示しました。


さて、これをさらに正当化しましょう。スマートUIにバインドするエンティティまたはモデルオブジェクトがあるとします。このスマートUI(これをShindows Mormsと呼びます)では、INotifyPropertyChangedを実装して、変更の追跡とそれに応じたUIの更新を行うことができます。

「わかりました、それほど難しく聞こえません」それであなたは書き始めます。

あなたはこれから始めます:

public class Customer
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime CustomerSince { get; set; }
    public string Status { get; set; }
}

..andで終わるこの

public class UglyCustomer : INotifyPropertyChanged
{
    private string _firstName;
    public string FirstName
    {
        get { return _firstName; }
        set
        {
            string oldValue = _firstName;
            _firstName = value;
            if(oldValue != value)
                OnPropertyChanged("FirstName");
        }
    }

    private string _lastName;
    public string LastName
    {
        get { return _lastName; }
        set
        {
            string oldValue = _lastName;
            _lastName = value;
            if(oldValue != value)
                OnPropertyChanged("LastName");
        }
    }

    private DateTime _customerSince;
    public DateTime CustomerSince
    {
        get { return _customerSince; }
        set
        {
            DateTime oldValue = _customerSince;
            _customerSince = value;
            if(oldValue != value)
                OnPropertyChanged("CustomerSince");
        }
    }

    private string _status;
    public string Status
    {
        get { return _status; }
        set
        {
            string oldValue = _status;
            _status = value;
            if(oldValue != value)
                OnPropertyChanged("Status");
        }
    }

    protected virtual void OnPropertyChanged(string property)
    {
        var propertyChanged = PropertyChanged;

        if(propertyChanged != null)
            propertyChanged(this, new PropertyChangedEventArgs(property));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

これはうんざりするような配管のコードであり、私があなたがそのようなコードを手で書いているなら、あなたはあなたのクライアントから盗んでいると私は主張します。より良い、よりスマートな作業方法があります。

その言葉を聞いたことがありますか?

あなたのチームの賢い人がやって来て言ったと想像してみてください:「ここに簡単な方法があります」

プロパティを仮想化すると(落ち着いて、それほど大きな問題ではありません)、プロパティの動作を自動的に織り込むことができます。(これはAOPと呼ばれますが、名前を気にする必要はありません。あなたのために何をするかに集中してください)

使用しているIoCツールに応じて、次のようなことができます。

var bindingFriendlyInstance = IoC.Resolve<Customer>(new NotifyPropertyChangedWrapper());

ひどい! 問題のオブジェクトのすべての仮想プロパティセッターで、そのすべての手動INotifyPropertyChanged BSが自動的に生成されます。

これは魔法ですか? はい!このコードが機能するという事実を信頼できる場合は、mumbo-jumboをラップするそのプロパティのすべてを安全にスキップできます。解決すべきビジネス上の問題があります。

AOPを実行するためのIoCツールのその他の興味深い使用法:

  • 宣言的でネストされたデータベーストランザクション
  • 宣言的でネストされた作業ユニット
  • ロギング
  • 事前/事後条件(契約による設計)

28
おっと、すべてのプロパティを仮想化するのを忘れていて、機能しませんでした。クライアントからのこの盗みのデバッグに費やす時間も必要ですか?(DynamicProxyを使用していない場合はお詫びします。:P)
Thom

17
INotifyPropertyChangedラッパーを使用して、多くの明快さを犠牲にします。最初にその配管を書くのに数分以上かかる場合、あなたは間違った仕事をしています。コードの冗長性に関しては、少なくても多くても多くありません!
2009年

39
@overstood-私は完全に同意しません。まず、この例ではどこがわかりやすいのでしょうか。最寄りの消費者。コンシューマーはINotifyPropertyChangedを明示的に要求しています。マイクロまたはマクロのコード生成でこのタイプのコードを作成できるからといって、それを書くのが良い考えだとは限りません。そのINotifyPropertyChangedの大げさなものは、まったくの平凡で、平凡なものであり、実際には問題のクラスの可読性を損ないます。
Ben Scheirman、2009年

31
Dependency InjectionパターンとService Locatorパターンを混同しているため、マークダウンしました。前者は後者の代わりに使用するためのものです。たとえば、オブジェクト(または実際にはシステム全体)は、Resolve(...)を呼び出してIShippingServiceのインスタンスを取得することはできません。IShippingServiceを必要とするオブジェクトは、作成時に使用するインスタンスへの参照を受け取る必要があり、いくつかの上位層は2つのオブジェクトを接続する役割を果たします。
ナット・

30
これが何であれ(IoC、DynamicProxy、AOP、または黒魔術)、誰かがこれを実現するための詳細を提供するフレームワーク内の具体的な記事/特定のドキュメントに私をリンクできますか?
fostandy

138

私はあなたと一緒です、ヴァディム。IoCコンテナは、シンプルでエレガント、そして便利なコンセプトを採用しており、200ページのマニュアルで2日間勉強する必要があります。

IoCコミュニティがMartin Fowlerによる美しくエレガントな記事を取り上げて、それを通常200〜300ページのマニュアルを備えた一連の複雑なフレームワークに変えた方法に私は個人的に困惑しています。

私は批判的にならないようにしていますが(HAHA!)、IoCコンテナーを使用する人々は(A)非常に賢く、(B)それほど賢くない人々に対する共感に欠けていると思います。すべてが完璧に理にかなっているので、多くの一般的なプログラマーが概念を混乱させると理解するのに苦労しています。それは知識呪いです。IoCコンテナを理解している人は、それを理解していない人がいるとは信じられません。

IoCコンテナーを使用する最も有益な利点は、構成モードを1か所に配置できることです。たとえば、テストモードと本番モードを切り替えることができます。たとえば、データベースアクセスクラスの2つのバージョンがあるとします。1つは積極的にログに記録し、開発中に使用した多くの検証を行ったバージョンと、ログや検証を行わずに本番環境で非常に高速なバージョンです。それらを1か所で切り替えることができると便利です。一方、これは、IoCコンテナーの複雑さなしに、より簡単な方法で簡単に処理できるかなり自明な問題です。

IoCコンテナーを使用すると、コードは正直なところ、非常に読みにくくなると思います。コードが何をしようとしているのかを理解するために見なければならない場所の数は、少なくとも1つ増えます。そして天国のどこかで天使が叫びます。


81
事実を少しジョエルに混同していると思います。IoCとコンテナが最初に来て、次にMartinによる論文が来ました。逆はそうではありません。
グレンブロック

55
ほとんどのコンテナは、非常に迅速に作業を進めることができます。autofac、構造マップ、単一性、ninjectなどを使用すると、コンテナーを約5分で動作させることができます。はい、それらは高度な機能を備えていますが、地面に降りるのにそれらは必要ありません。
グレンブロック

58
ジョエル私はあなたと同じように考えていました。それから私は文字通り、真剣に5分間かけて最も基本的なセットアップを実行する方法を理解し、80/20ルールの動作に驚きました。特に単純なケースでは、コードがよりすっきりします。
ジャスティンボゾニエ

55
私は2年未満のプログラミングをしていて、Ninject wikiを読んでそれを手に入れました。それ以来、私は多くの場所でDIを使用しています。あれ見た?2年未満で、wikiの数ページを読んでいる。私は魔法使いでも何でもない。自分は天才ではないと思いますが、理解しやすかったです。OOを本当に理解している人がOOを把握できないとは思えません。また、あなたが参照している200-300ページのマニュアルは何ですか?私はそれらを見たことがありません。私が使用したすべてのIoCコンテナーはかなり短く、要点があります。
JRガルシア

35
+1よく書かれ、考え抜かれた。多くの人が気付いていないことの1つは、何かを「魔法のように」処理するためにフレームワークなどを追加すると、システムに複雑さと制限が自動的に追加されることです。時にはそれは価値がありますが、しばしば何かが見落とされ、エントロピーが制限によって強制された問題を修正しようとするコードにリリースされます。事前に時間を節約できるかもしれませんが、あいまいな問題を修正しようとすると、100倍のコストがかかる可能性があることを人々は認識しなければなりません。
kemiller2002、2009年

37

おそらく、DIコンテナーフレームワークを使用するように強制されている人はいないでしょう。クラスを分離してテスト容易性を向上させるためにすでにDIを使用しているため、多くのメリットを得ています。要するに、一般的には良いことであるシンプルさを好むのです。

システムが、手動DIが面倒になる(つまり、メンテナンスが増える)複雑なレベルに達した場合は、DIコンテナーフレームワークのチーム学習曲線と比較検討してください。

依存関係のライフタイム管理をより詳細に制御する必要がある場合(つまり、シングルトンパターンを実装する必要性を感じた場合)、DIコンテナーを調べてください。

DIコンテナーを使用する場合は、必要な機能のみを使用してください。XML構成ファイルをスキップし、それで十分であれば、コードで構成します。コンストラクター注入に固執する。UnityまたはStructureMapの基本は、いくつかのページに要約できます。

これについては、Mark Seemannによる素晴らしいブログ投稿があります。DI コンテナを使用する場合


非常に良い反応。私が意見を異にする唯一のことは、手動注射がこれまでそれほど複雑ではないか、雑用ではないと考えることができるかどうかです。
wekempf 2009年

+1。ここで最良の答えの1つ。ブログ記事へのリンクもありがとうございます。
stakx-2013年

1
バランスのとれた視点の場合は+1。IoCコンテナは常に良いとは限りませんし、常に悪いとは限りません。必要がなければ、使用しないでください。必要に応じて、ツールが存在することを確認してください。
Phil

32

私の意見では、IoCの最大の利点は、依存関係の構成を集中管理できることです。

現在依存性注入を使用している場合、コードは次のようになります。

public class CustomerPresenter
{
  public CustomerPresenter() : this(new CustomerView(), new CustomerService())
  {}

  public CustomerPresenter(ICustomerView view, ICustomerService service)
  {
    // init view/service fields
  }
  // readonly view/service fields
}

静的IoCクラスを使用した場合は、より複雑な構成ファイルであるIMHOとは異なり、次のようになります。

public class CustomerPresenter
{
  public CustomerPresenter() : this(IoC.Resolve<ICustomerView>(), IoC.Resolve<ICustomerService>())
  {}

  public CustomerPresenter(ICustomerView view, ICustomerService service)
  {
    // init view/service fields
  }
  // readonly view/service fields
}

次に、静的IoCクラスは次のようになります。ここではUnityを使用しています。

public static IoC
{
   private static readonly IUnityContainer _container;
   static IoC()
   {
     InitializeIoC();
   }

   static void InitializeIoC()
   {
      _container = new UnityContainer();
      _container.RegisterType<ICustomerView, CustomerView>();
      _container.RegisterType<ICustomerService, CustomerService>();
      // all other RegisterTypes and RegisterInstances can go here in one file.
      // one place to change dependencies is good.
   }
}

11
ベン、あなたが話しているのは、依存関係の注入ではなく実際のサービスの場所です。違いがあります。私はいつも後者より前者を好む。stevenharman.net/blog/archive/2009/09/25/...
stevenharman

23
デフォルトのコンストラクターでIoC.Resolve <T>を実行する代わりに、IOCコンテナーの機能を利用してオブジェクトを構築する必要があります。その空のコンストラクターを取り除き、IOCコンテナーにオブジェクトを作成させます。var presenter = IoC.Resolve <CustomerPresenter>(); 出来上がり!すべての依存関係はすでに関連付けられています。
Ben Scheirman、2009年

12
この種の構成を一元化することのマイナス面は、知識の非コンテキスト化です。Joelはこの問題を「コードは正直なところ、非常に読みにくくなる」と指摘しています。
Scott Bellware、2009年

6
@sbellware、どんな知識?主要な依存関係として使用されるインターフェースは契約として機能し、その目的と動作の両方を伝えるのに十分でなければなりませんか?コントラクトの実装への洞察から得た知識を参照していると思いますが、その場合、適切な構成を追跡する必要がありますか?グラフのノードとエッジをナビゲートするのが得意なので、コンピューター(VS、IntelliJなどのR#)に依存しています。現在集中している作業現場の状況に合わせて、脳のスタックを予約しています。
stevenharman、2009年

7
スティーブ、私は.NETテキストを知っており、そのウォークを何年も歩いていました。また、他のモードや形式にも精通しており、実際にはトレードオフがあることを直感的に理解しています。私はこれらのトレードオフをいつどのようにして行うかを知っていますが、私の仕事生活の多様性により、少なくともジョエルの視点を明確に理解できます。ほとんどの.NETモノカルチャリストより長い間使用してきたツールやトリックについて説明するのではなく、知らないことを教えてください。私はalt.netスタシスや正統性ではなく、多様で批判的な思考のために私の脳のスタックを予約しています。
スコットベルウェア、2009年

32

IoCコンテナは、深くネストされたクラスの依存関係を読み込むのにも適しています。たとえば、Depedency Injectionを使用して次のコードがあるとします。

public void GetPresenter()
{
    var presenter = new CustomerPresenter(new CustomerService(new CustomerRepository(new DB())));
}

class CustomerPresenter
{
    private readonly ICustomerService service;
    public CustomerPresenter(ICustomerService service)
    {
        this.service = service;
    }
}

class CustomerService
{
    private readonly IRespository<Customer> repository;
    public CustomerService(IRespository<Customer> repository)
    {
        this.repository = repository;
    }
}

class CustomerRepository : IRespository<Customer>
{
    private readonly DB db;
    public CustomerRepository(DB db)
    {
        this.db = db;
    }
}

class DB { }

これらの依存関係のすべてをIoCコンテナーに読み込んでいる場合、CustomerServiceを解決すると、すべての子の依存関係が自動的に解決されます。

例えば:

public static IoC
{
   private IUnityContainer _container;
   static IoC()
   {
       InitializeIoC();
   }

   static void InitializeIoC()
   {
      _container = new UnityContainer();
      _container.RegisterType<ICustomerService, CustomerService>();
      _container.RegisterType<IRepository<Customer>, CustomerRepository>();
   }

   static T Resolve<T>()
   {
      return _container.Resolve<T>();
   }
}

public void GetPresenter()
{
   var presenter = IoC.Resolve<CustomerPresenter>();
   // presenter is loaded and all of its nested child dependencies 
   // are automatically injected
   // -
   // Also, note that only the Interfaces need to be registered
   // the concrete types like DB and CustomerPresenter will automatically 
   // resolve.
}

ただし、IoCクラスは、サービスロケーターのアンチパターンを使用して呼び出されます。静的T Resolve <T>()を呼び出す代わりに、コンストラクターDIを使用してshould_containerを渡す必要がありますか?
Michael Freidgeim 2013年

@bendeweyこの例は、新しいCustomerServiceおよびnew CustomerRepositoryのコンストラクターに引数を自動的に渡すことを意味しています。これはどのように機能しますか?他に追加のパラメータがある場合はどうなりますか?
Brain2000 2013

28

私は宣言型プログラミングのファンです(私が答えるSQLの質問の数を見てください)。

...またはおそらくIoCコンテナーの開発者は、明確なドキュメントを書くことができません。

...または両方ともある程度正しい。

IoCコンテナーの概念は悪くないと思います。ただし、実装は多種多様なアプリケーションで役立つだけの強力さ(つまり、柔軟性)を備えている必要がありますが、シンプルで簡単に理解できます。

それはおそらく他の6分の1と半ダースです。実際のアプリケーション(おもちゃやデモではありません)は複雑で、多くのコーナーケースとルールの例外を考慮に入れる必要があります。その複雑さを命令型コードにカプセル化するか、宣言型コードにカプセル化します。しかし、どこかにそれを表す必要があります。


5
複雑さの避けられない存在についてのあなたの観察が好きです。コンテナベースの依存関係と手動の依存関係のワイヤリングの違いは、コンテナを使用すると、各コンポーネントに個別に集中できるため、増加する複雑さをかなり直線的に管理できることです。1つのコンポーネントを構成する動作を他のすべての構成から分離するのは難しいため、手動で依存関係を配線するシステムは、進化するのがより困難です。
Nicholas Blumhardt

AspectJが好きです。これはJavaではなく、XMLでもありません。それは一種のIS Javaであり、Javaの拡張であると感じ、Javaのように見えます。POJOやビジネスロジックに自分の側面を入れたくない。IoCも除外したいところです。IMHO、ツールの関連付けにXMLが完全に多すぎる。設定ファイルが必要な場合は、それらをBeanShellスクリプトにします。/ Java、ここで.Netの動作を適用します->。
Chris K

2
私は確かにあなたの答えを理解していますが、問題の大部分は、多くのIoCフレームワーク(そして実際には、他のもののための他の多くのフレームワーク)が、必要な概念と用語。私は、他のプログラマーのコードを継承したばかりのことについて多くを学んでいます。「オブジェクトグラフ」がかなり小さく、コードの実際のテストカバレッジがない場合に、コードがIoCを使用する理由を理解するのに苦労しています。SQLプログラマーとしては、IoCをORMとともに使用することをお勧めします。
ケニーエビット2013

1
@KennyEvitt、良い点、シンプルなアプリがあり、テストカバレッジが十分でなくても、IoCフレームワークを使用するのは時期尚早のようです。
Bill Karwin 2013

27

コンテナーの使用は、主に、命令型/スクリプト形式の初期化と構成から宣言型に変更することです。これにはいくつかの異なる有益な効果があるかもしれません:

  • 毛玉のメインプログラムの起動ルーチンを減らします。
  • かなり深い展開時の再構成機能を有効にします。
  • 依存性注入型スタイルを、新しい作業に対する抵抗が最も少ない経路にする。

もちろん、困難があるかもしれません:

  • 複雑な起動/シャットダウン/ライフサイクル管理を必要とするコードは、コンテナに簡単に適合させることができない場合があります。
  • おそらく、個人的、プロセス、およびチームカルチャーの問題をナビゲートする必要がありますが、それが理由です...
  • 一部のツールキットは急速に重くなり、多くのDIコンテナーが反発として始めた深い依存関係を奨励しています。

23

以下のようにそれは私に聞こえるあなたはすでに、独自のIoCコンテナを構築しましたし、他の誰かの実装では、より良いあなたよりである理由を求めている(Martin Fowler氏によって記述された様々なパターンを使用して)。

したがって、すでに機能するコードの束があります。そして、なぜそれを他の誰かの実装に置き換えたいのか疑問に思っています。

サードパーティのIoCコンテナーを検討することの長所

  • バグは無料で修正されます
  • ライブラリのデザインはあなたのものより良いかもしれません
  • 人々はすでに特定のライブラリに精通しているかもしれません
  • ライブラリはあなたのものより速いかもしれません
  • 実装したい機能がいくつかあるかもしれませんが、実行する時間がありませんでした(サービスロケータはありますか?)

短所

  • バグは無料で紹介されます:)
  • ライブラリのデザインはあなたのものより悪いかもしれません
  • あなたは新しいAPIを学ぶ必要があります
  • 使用しない機能が多すぎる
  • 通常、作成していないコードをデバッグすることは困難です
  • 以前のIoCコンテナーからの移行は面倒な場合があります

だから、あなたの長所と短所を比較検討し、決定を下してください。


私はあなたに同意します。サム-DIはIoCの一種です。したがって、元のポスターがDIを実行している場合、彼らはすでにIoCを実行しているので、実際の問題はなぜ自分自身をロールバックするかです。
ジェイミーラブ

3
同意しません。IOCはDIを行う方法です。IOCコンテナーは、動的ランタイムリフレクションを使用して型のインスタンス化を制御することでDIを実行し、依存関係を満たし、注入します。OPは、彼が静的DIを実行していることを示唆しており、IOCの動的ランタイムリフレクションに関連することが多いメリットとコンパイル時チェックのメリットをトレードオフする必要がある理由を考えています。
Maxm007 2013年

17

IoCの価値のほとんどは、DIを使用することで得られると思います。あなたはすでにそうしているので、残りのメリットは段階的に増えます。

得られる値は、作業しているアプリケーションのタイプによって異なります。

  • マルチテナントの場合、IoCコンテナーは、さまざまなクライアントリソースを読み込むためのインフラストラクチャコードの一部を処理できます。クライアント固有のコンポーネントが必要な場合は、カスタムセレクターを使用してロジックを処理し、クライアントコードからそれを心配する必要はありません。これは確かに自分で構築できますが、IoCがどのように役立つかを示す例を次に示します。

  • IoCは多くの拡張性ポイントを備えているため、構成からコンポーネントをロードするために使用できます。これは一般的なビルド方法ですが、ツールはコンテナによって提供されます。

  • 横断的な懸念のためにAOPを使用する場合、IoCはメソッド呼び出しをインターセプトするフック提供します。これはプロジェクトではあまり行われませんが、IoCを使用すると簡単になります。

以前にこのような機能を記述したことがありますが、これらの機能のいずれかが必要になった場合は、アーキテクチャに適合している場合は、ビルド済みのテスト済みツールを使用します。

他の人が述べたように、使用するクラスを一元的に構成することもできます。これは良いことですが、誤った方向付けと複雑さを犠牲にしています。ほとんどのアプリケーションのコアコンポーネントはあまり置き換えられていないため、トレードオフを行うのは少し難しいです。

私はIoCコンテナーを使用して機能性を高く評価していますが、トレードオフに気付いたことを認める必要があります。コードはクラスレベルでより明確になり、アプリケーションレベルでは明確ではなくなります(つまり、制御フローを視覚化します)。


16

私は回復中のIOC中毒者です。最近、ほとんどの場合、DIにIOCを使用することを正当化するのは難しいと感じています。IOCコンテナーはコンパイル時間のチェックを犠牲にしており、おそらく見返りに、「簡単な」セットアップ、複雑なライフタイム管理、および実行時の依存関係のオンザフライでの提供を提供します。コンパイル時間のチェックとその結果としての実行時のマジック/例外が失われることは、大多数のケースで一見の価値があるものではありません。大規模なエンタープライズアプリケーションでは、何が起こっているのかを追跡することが非常に困難になる可能性があります。

アプリケーションに抽象ファクトリを使用し、オブジェクトの作成を抽象ファクトリに忠実に延期する(つまり、適切なDIを行う)ことで、静的セットアップを非常に簡単に集中できるため、集中引数を購入しません。

このように、マジックフリーの静的DIを実行してみませんか。

interface IServiceA { }
interface IServiceB { }
class ServiceA : IServiceA { }
class ServiceB : IServiceB { }

class StubServiceA : IServiceA { }
class StubServiceB : IServiceB { }

interface IRoot { IMiddle Middle { get; set; } }
interface IMiddle { ILeaf Leaf { get; set; } }
interface ILeaf { }

class Root : IRoot
{
    public IMiddle Middle { get; set; }

    public Root(IMiddle middle)
    {
        Middle = middle;
    }

}

class Middle : IMiddle
{
    public ILeaf Leaf { get; set; }

    public Middle(ILeaf leaf)
    {
        Leaf = leaf;
    }
}

class Leaf : ILeaf
{
    IServiceA ServiceA { get; set; }
    IServiceB ServiceB { get; set; }

    public Leaf(IServiceA serviceA, IServiceB serviceB)
    {
        ServiceA = serviceA;
        ServiceB = serviceB;
    }
}


interface IApplicationFactory
{
    IRoot CreateRoot();
}

abstract class ApplicationAbstractFactory : IApplicationFactory
{
    protected abstract IServiceA ServiceA { get; }
    protected abstract IServiceB ServiceB { get; }

    protected IMiddle CreateMiddle()
    {
        return new Middle(CreateLeaf());
    }

    protected ILeaf CreateLeaf()
    {
        return new Leaf(ServiceA,ServiceB);
    }


    public IRoot CreateRoot()
    {
        return new Root(CreateMiddle());
    }
}

class ProductionApplication : ApplicationAbstractFactory
{
    protected override IServiceA ServiceA
    {
        get { return new ServiceA(); }
    }

    protected override IServiceB ServiceB
    {
        get { return new ServiceB(); }
    }
}

class FunctionalTestsApplication : ApplicationAbstractFactory
{
    protected override IServiceA ServiceA
    {
        get { return new StubServiceA(); }
    }

    protected override IServiceB ServiceB
    {
        get { return new StubServiceB(); }
    }
}


namespace ConsoleApplication5
{
    class Program
    {
        static void Main(string[] args)
        {
            var factory = new ProductionApplication();
            var root = factory.CreateRoot();

        }
    }

    //[TestFixture]
    class FunctionalTests
    {
        //[Test]
        public void Test()
        {
            var factory = new FunctionalTestsApplication();
            var root = factory.CreateRoot();
        }
    }
}

コンテナー構成は抽象ファクトリー実装であり、登録は抽象メンバーの実装です。新しいシングルトン依存関係が必要な場合は、抽象ファクトリに別の抽象プロパティを追加するだけです。一時的な依存関係が必要な場合は、別のメソッドを追加して、Func <>として挿入します。

利点:

  • すべてのセットアップとオブジェクト作成の構成は一元化されています。
  • 構成は単なるコードです
  • コンパイル時間のチェックにより、登録の更新を忘れることがないため、メンテナンスが容易になります。
  • ランタイムリフレクションマジックなし

私は懐疑論者に、次のグリーンフィールドプロジェクトを開始し、どの時点でコンテナーが必要かを正直に自問することをお勧めします。ファクトリー実装をIOCコンテナー構成モジュールに置き換えるだけなので、後でIOCコンテナーを組み込むのは簡単です。


これはIoCに対する議論のようには思えませんが、構成可能な IoCフレームワークに対する議論のようです。ここの工場は、単純にハードコーディングされた IoCに要約されませんか?
Mr.Mindor 2013

ポスターがIoCについて言及したとき、IoCコンテナーフレームワークが意図されていたと思います。元の記事をありがとう、これは私がフレームワークなしで行くことを納得させるのに役立ちます。それで、テストおよび本番コードで構成可能である場合、「構成可能」フレームワークの利点は何ですか?
ハギー2013

用語の理解からすると、Inversion Of Controlは実行時に依存関係を見つけることを意味します。つまり、ファクトリーは基本的にハードコーディングされたコンテナーまたは静的コンテナーであると言えますが、IOCコンテナーではありません。タイプはコンパイル時に解決されます。これは、辞書検索を使用して実行時に型を解決する構成可能なフレームワークを使用することに対する反対意見です。
Maxm007 2014年

14

IoCコンテナーを使用することの最大の利点(個人的には、私はNinjectを使用しています)は、設定や他の種類のグローバル状態オブジェクトの受け渡しを排除することです。

私はWeb用にプログラムを作成していません。私の場合はコンソールアプリケーションであり、オブジェクトツリーの多くの場所で、オブジェクトツリーの完全に別のブランチで作成された、ユーザーが指定した設定またはメタデータにアクセスする必要があります。IoCを使用して、設定をシングルトンとして処理するようにNinjectに指示し(設定は常に1つしかないため)、コンストラクターとプレストで設定または辞書を要求します...必要なときに魔法のように表示されます!

IoCコンテナーを使用しない場合、設定やメタデータを必要とするオブジェクトによって実際に使用される前に、2、3、...、n個のオブジェクトに渡す必要があります。

他の人々がここで詳しく説明しているように、DI / IoCコンテナには他にも多くの利点があります。オブジェクトの作成からオブジェクトの要求に移るのは大変なことですが、DIを使用することは私と私のチームにとって非常に役立ちました。あなたの武器に!


2
これは大きなプラスです。他の誰も以前にそれを言及したことが信じられない。一時オブジェクトを渡したり格納したりする必要がないため、コードがよりクリーンになります。
Finglas

1
@qbleグローバルオブジェクトはうまく機能します...コードベースを緊密に結合されたテスト不可能な悪夢にしたい場合は、時間の経過とともに変更が難しくなります。頑張ってください。
ジェフリーキャメロン

2
@JeffreyCameron IoCコンテナグローバルオブジェクトです。グローバルな依存関係は削除されません。
qble 2013

3
確かに、IoCコンテナはアプリケーションの他の部分に対して透過的です。起動時に一度呼び出してインスタンスを取得すると、それが最後に表示されます。グローバルオブジェクトを使用すると、コードにハード依存が散らばります
Jeffrey Cameron

1
@qbleは、ファクトリーを使用してオンザフライで一時インスタンスを作成します。工場はシングルトンサービスです。一時インスタンスがIOCコンテナーから何かを必要とする場合、それらをファクトリーにワイヤリングし、ファクトリーに依存関係を一時インスタンスに渡します。
ジェフリーキャメロン

14

IoCフレームワークは、必要に応じて優れています...

  • ...タイプセーフを破棄します。多くの(すべて?)IoCフレームワークでは、すべてが正しく接続されていることを確認したい場合は、コードを実行する必要があります。「ねえ!これらの100個のクラスの初期化がプロダクションで失敗せず、nullポインター例外がスローされるように、すべてをセットアップしてほしいと思います!」

  • ...コードをグローバルで散らかす(IoCフレームワークはすべてグローバル状態の変更に関するものです)。

  • ...何が何に依存しているのかわからないので、リファクタリングが難しい不明確な依存関係でくだらないコードを記述します。

IoCの問題は、IoCを使用するユーザーがこのようなコードを作成していたことです。

public class Foo {
    public Bar Apa {get;set;}
    Foo() {
        Apa = new Bar();
    }
}

FooとBarの間の依存関係はハードワイヤードであるため、これには明らかに欠陥があります。次に、彼らは次のようなコードを書く方が良いだろうと気づきました

public class Foo {
    public IBar Apa {get;set;}
    Foo() {
        Apa = IoC<IBar>();
    }
}

これにも欠陥がありますが、それほど明確ではありません。Haskellではのタイプは次のFoo()ようになりますIO Fooが、IO-part は実際には必要ありません。これは、設計に問題がある場合に、設計に問題があることを示す警告サインになるはずです。

それ(IO部分)を取り除くには、IoCフレームワークのすべての利点を取得します。代わりに、抽象ファクトリを使用することのできる欠点はありません。

正しい解決策は次のようなものです

data Foo = Foo { apa :: Bar }

または多分

data Foo = forall b. (IBar b) => Foo { apa :: b }

そしてインジェクト(しかし、私はそれをインジェクトとは呼ばないでしょう)バー。

また、このビデオを参照してください。ErikMeijer(LINQの発明者)は、DIは数学を知らない(そして私はこれ以上同意できない)人々向けであると述べています。http//www.youtube.com/watch?v = 8Mttjyf-8P4

スポルスキー氏とは異なり、IoCフレームワークを使用する人々がとても賢いとは思いません。数学を知らないだけだと思います。


9
型安全性を捨てる-あなたの例がまだ型安全であることを除いて、それがインターフェースです。オブジェクトのタイプを渡すのではなく、インターフェースのタイプを渡します。とにかく、タイプセーフはnull参照の例外を取り除きません。null参照をタイプセーフパラメーターとして渡すことができます。これは、タイプセーフの問題ではありません。
blowdart 2009年

IoCコンテナー内で型が関連付けられているかどうかがわからないため、タイプセーフティを破棄します(IoC <IBar>の型はでなくIBar、実際にはIO IBarです。)そして、Haskellの例ではnull参照を取得できません。
finnsson

型の安全性は、オブジェクトが結び付けられていることについてではなく、少なくともJava / C#ではそうではありません。それは、オブジェクトが適切な型であることに関するものです。そして、ConcreteClass myClass = nullはまだ正しいタイプです。null以外を強制したい場合は、コードコントラクトが有効になります。
blowdart

2
あなたの第一の点にある程度同意します。第二と第三の説明は私にとって決して問題ではありません。C#サンプルでは、​​よくある間違いであるIoCコンテナーをサービスロケーターとして使用しています。そして、C#とHaskellの比較=リンゴとオレンジ。
Mauricio Scheffer、

2
タイプセーフを捨てないでください。一部の(ほとんどではない)DIコンテナーでは、登録プロセス後に完全なオブジェクトグラフを確認できます。これを行うと、アプリケーションが誤った構成で起動するのを防ぐことができます(フェイルファスト)。私のアプリケーションには、本番構成を呼び出すいくつかの単体テストがあり、テスト中にこれらのエラーを見つけることができます。IoCコンテナーを使用しているすべてのトップレベルのオブジェクトを解決できることを確認するいくつかの単体テストを作成するために、皆さんに助言します。
スティーブン

10

Dependency Injectionを正しく実装すると、プログラマーはコードのテスト容易性、柔軟性、保守性、およびスケーラビリティーの向上に役立つさまざまな他のプログラミング手法を使用する傾向があることがわかりました:単一責任原則、懸念の分離、コーディングなどの実践APIに対して。一口サイズのチャンクでコードを取得できるので、コードを読みやすくする、よりモジュール化された一口サイズのクラスとメソッドを書く必要があるように感じます。

ただし、かなり大きな依存関係ツリーを作成する傾向もあります。依存関係ツリーは、フレームワークを介して(特に、規則を使用する場合)手作業よりもはるかに簡単に管理できます。今日、私はLINQPadで非常に迅速に何かをテストしたかったのですが、カーネルを作成してモジュールにロードするのは面倒なので、手作業でこれを書きました。

var merger = new SimpleWorkflowInstanceMerger(
    new BitFactoryLog(typeof(SimpleWorkflowInstanceMerger).FullName), 
    new WorkflowAnswerRowUtil(
        new WorkflowFieldAnswerEntMapper(),
        new ActivityFormFieldDisplayInfoEntMapper(),
        new FieldEntMapper()),
    new AnswerRowMergeInfoRepository());

振り返ってみると、IoCフレームワークを使用する方が迅速だったでしょう。なぜなら、モジュールは慣例により、これらのほとんどすべてを定義しているからです。

この質問に対する回答とコメントの調査に少し時間を費やして、IoCコンテナーの使用に反対する人々は真の依存性注入を実践していないと確信しています。私が見た例は、依存関係の注入とよく混同されるプラクティスの例です。一部の人々は、コードを「読み取る」ことが困難であると不平を言っています。正しく行われていれば、手作業でDIを使用する場合とIoCコンテナーを使用する場合のコードの大部分は同じです。違いは完全にアプリケーション内のいくつかの「開始点」にあるはずです。

言い換えると、IoCコンテナが気に入らない場合は、依存性注入が想定されている方法で実行されていない可能性があります。

別のポイント:どこかでリフレクションを使用する場合、依存性注入は実際には手動では実行できません。コードナビゲーションに対するリフレクションの機能は嫌いですが、避けられない特定の領域があることを認識しておく必要があります。たとえば、ASP.NET MVCは、各リクエストのリフレクションを介してコントローラーをインスタンス化しようとします。依存関係の注入を手動で行うには、次のように、すべてのコントローラーを「コンテキストルート」にする必要があります。

public class MyController : Controller
{
    private readonly ISimpleWorkflowInstanceMerger _simpleMerger;
    public MyController()
    {
        _simpleMerger = new SimpleWorkflowInstanceMerger(
            new BitFactoryLog(typeof(SimpleWorkflowInstanceMerger).FullName), 
            new WorkflowAnswerRowUtil(
                new WorkflowFieldAnswerEntMapper(),
                new ActivityFormFieldDisplayInfoEntMapper(),
                new FieldEntMapper()),
            new AnswerRowMergeInfoRepository())
    }
    ...
}

これを、DIフレームワークで実行できるようにすることと比較してください。

public MyController : Controller
{
    private readonly ISimpleWorkflowInstanceMerger _simpleMerger;
    public MyController(ISimpleWorkflowInstanceMerger simpleMerger)
    {
        _simpleMerger = simpleMerger;
    }
    ...
}

DIフレームワークを使用すると、次のことに注意してください。

  • このクラスを単体テストできます。モックを作成することによりISimpleWorkflowInstanceMerger、データベース接続などを必要とせずに、モックが予想どおりに使用されることをテストできます。
  • 私ははるかに少ないコードを使用しており、コードは非常に読みやすくなっています。
  • 私の依存関係の1つが変更された場合、コントローラーを変更する必要はありません。これは、複数のコントローラーが同じ依存関係の一部を使用する可能性が高いと考える場合に特に便利です。
  • データレイヤーからクラスを明示的に参照することはありません。私のWebアプリケーションには、ISimpleWorkflowInstanceMergerインターフェースを含むプロジェクトへの参照を含めることができます。これにより、アプリケーションを個別のモジュールに分割し、真の多層アーキテクチャーを維持できるため、物事がより柔軟になります。

典型的なWebアプリケーションには、かなりの数のコントローラーがあります。アプリケーションが成長するにつれて、各コントローラーで手作業でDIを行うことのすべての苦痛は、実際に増大します。リフレクションによってサービスをインスタンス化しようとしないコンテキストルートが1つしかないアプリケーションがある場合、これはそれほど大きな問題ではありません。それでも、依存関係グラフを管理するために何らかのフレームワークを使用しない限り、依存性注入を使用するアプリケーションは、特定のサイズに達すると管理に非常にコストがかかります。


9

「新しい」キーワードを使用するときはいつでも、具体的なクラス依存関係を作成していて、小さな警報ベルが頭の中で鳴るはずです。このオブジェクトを単独でテストすることは困難になります。解決策は、インターフェイスにプログラムし、依存関係を注入して、オブジェクトをそのインターフェイスを実装するもの(モックなど)で単体テストできるようにすることです。

問題は、どこかにオブジェクトを構築しなければならないことです。Factoryパターンは、POXO(Plain Oldの「ここにOO言語をここに挿入する」オブジェクト)からカップリングをシフトする1つの方法です。あなたとあなたの同僚がすべてこのようなコードを書いているなら、IoCコンテナはあなたのコードベースにできる次の「インクリメンタルな改善」です。厄介なFactoryボイラープレートコードをすべて、クリーンなオブジェクトとビジネスロジックからシフトします。彼らはそれを手に入れ、愛するでしょう。一体、会社があなたにそれを愛する理由について話して、みんなを熱狂させてください。

同僚がまだDIを行っていない場合は、最初にDIに集中することをお勧めします。簡単にテストできるクリーンなコードの書き方を広めます。クリーンなDIコードは難しい部分です。いったんそこに着くと、オブジェクトワイヤリングロジックをFactoryクラスからIoCコンテナーにシフトすることは比較的簡単です。


8

すべての依存関係が明確に表示されるため、疎結合であり、同時にアプリケーション全体で簡単にアクセスおよび再利用できるコンポーネントの作成を促進します。


7

IoCコンテナは必要ありません。

しかし、DIパターンに厳密に従っている場合は、DIパターンを使用すると、冗長な退屈なコードが大量に削除されることがわかります。

とにかく、ライブラリ/フレームワークを使用するのに最適なタイミングです。ライブラリの機能を理解し、ライブラリなしで実行できる場合です。


6

私はたまたま自分で作ったDIコードを取り除き、それをIOCに置き換えている最中です。おそらく200行を超えるコードを削除し、約10行に置き換えました。はい、コンテナ(Winsor)の使用方法について少し学習する必要がありましたが、私はインターネット技術に取り組んでいるエンジニアです21世紀なので、私はそれに慣れています。方法をじっくりと見て20分くらい過ごしたと思います。これは私の時間に値するものでした。


3
「しかし、私は21世紀にインターネット技術に取り組んでいるエンジニアなので、それに慣れている」-> +1
user96403

5

クラスの分離と依存関係の反転を続けると、クラスは小さくなり続け、「依存関係グラフ」のサイズは大きくなります。(これは悪いことではありません。)IoCコンテナーの基本機能を使用すると、これらすべてのオブジェクトを簡単に接続できますが、手動で行うと非常に負担が大きくなります。たとえば、「Foo」の新しいインスタンスを作成したいが、「バー」が必要な場合はどうでしょうか。また、「バー」には「A」、「B」、「C」が必要です。そして、それらのそれぞれは3つの他のものなどを必要とします(そうです、私は良い偽の名前を思い付くことができません:))。

IoCコンテナーを使用してオブジェクトグラフを作成すると、複雑さが大幅に軽減され、1回限りの構成にプッシュされます。「Create a a 'Foo'」と言うだけで、ビルドに必要なものがわかります。

IoCコンテナーをはるかに多くのインフラストラクチャに使用している人もいますが、これは高度なシナリオでは問題ありませんが、その場合、コードを難読化し、新しい開発者がコードを読みにくくデバッグしにくくなる可能性があることに同意します。


1
なぜ私たちはそれらを引き上げるのではなく、最も一般的な分母をなだらかにし続けるのですか?
stevenharman、2009年

4
私は緩和について何も言わなかった。先ほど述べたように、高度なシナリオを使用すると、新しい開発者にとってわかりにくくなる可能性があります。これは事実です。「しないで」とは言わなかった。しかし、それでも(悪魔の支持者の時)、コードベースをより明確で親しみやすくする同じことを達成する他の方法がしばしばあります。インフラストラクチャツールのカスタム構成の複雑さを隠すことができるのは、それが正しい理由ではないからです。
アーロン・レルチ、

4

Unityに関するDittos。大きくしすぎると、垂木にきしむ音が聞こえます。

IoCコードがどれほどきれいに見えるかについて人々が噴き出し始めたとき、私が驚くことは決してありません。かつてC ++のテンプレートが90年代に戻るためのエレガントな方法であった方法についてかつて話した人々と同じ種類の人々ですが、今日ではそれらを難解なものとして非難するでしょう。ば!


2

.NETの世界では、AOPはあまり人気がないため、DIを作成する場合でも、別のフレームワークを使用する場合でも、DIではフレームワークが唯一の現実的な選択肢です。

AOPを使用した場合は、アプリケーションのコンパイル時に注入できます。これは、Javaではより一般的です。

単体テストが容易になるようにカップリングを減らすなど、DIには多くの利点がありますが、それをどのように実装しますか?リフレクションを使用して自分で行いますか?


新しいMEFフレームワークでは.NETでもこれが可能になり、コードがより簡潔で理解しやすくなり、DIの概念を非常に理解しやすくなります。
terjetyl、2009年

4
@TT:MEFはありません依存性注入コンテナとAOPとは何の関係もありません。
Mauricio Scheffer、

2

だから、ほぼ3年になりますね。

  1. IoCフレームワークを支持してコメントした人の50%は、IoCとIoCフレームワークの違いを理解していません。アプリサーバーにデプロイせずにコードを記述できることを彼らが知っているとは思えません

  2. 最も人気のあるJava Springフレームワークを使用すると、IoC構成がXMlからコードに移動され、次のようになります。

`@Configuration public class AppConfig {

public @Bean TransferService transferService() {
    return new TransferServiceImpl(accountRepository());
}

public @Bean AccountRepository accountRepository() {
    return new InMemoryAccountRepository();
}

} `そして、これを行うためのフレームワークが必要なのはなぜですか。


1

正直なところ、IoCコンテナーが必要になるケースは多くありません。たいていの場合、それらは不要な複雑さを追加するだけです。

オブジェクトの構築を簡単にするためだけに使用している場合は、このオブジェクトを複数の場所でインスタンス化していますか?シングルトンはあなたのニーズに合わないでしょうか?実行時に構成を変更していますか?(データソースタイプの切り替えなど)。

はいの場合、IoCコンテナが必要になる可能性があります。そうでない場合は、開発者が簡単に確認できる場所から初期化を移動しているだけです。

とにかく、インターフェイスは継承よりも優れていると誰が言ったのでしょうか。サービスをテストしているとしましょう。なぜコンストラクターDIを使用せず、継承を使用して依存関係のモックを作成しますか?私が使用するほとんどのサービスには、いくつかの依存関係があります。無用のインタフェース手段のトンを維持するこの方法の防止をユニットテストを行うと、あなたはしていない持って迅速にReSharperのを使用する方法の宣言を見つけること。

ほとんどの実装では、IoCコンテナが不要なコードを削除すると言うのは神話だと思います。

まず、最初にコンテナを設定します。次に、初期化する必要がある各オブジェクトを定義する必要があります。したがって、初期化時にコードを保存せずに、コードを移動します(オブジェクトが複数回使用されている場合を除きます。シングルトンとしてより優れていますか?)。次に、この方法で初期化したオブジェクトごとに、インターフェースを作成して維持する必要があります。

誰かこれについて何か考えがありますか?


小規模なプロジェクトの場合でも、構成をXMLにすることで、非常にクリーンでモジュール化され、コードが再利用可能になります(接着剤で汚染されなくなったため)。適切なソフトウェア製品のShippingCostCalculator場合ProductUIPresentation、変更されたXMLファイルを1つだけデプロイすることにより、まったく同じコードベース内で、たとえば、またはその他の戦略/実装を構成または交換できます。
トーマスW

一般的に、それが完全に実装されている場合は、複雑さを増すのではなく、増すと思います。実行時に何かを交換する必要がある場合、それらは素晴らしいですが、なぜ難読化を追加するのですか?切り替えられないもののために、追加のインターフェースのオーバーヘッドを保持する理由は何ですか?テストにIoCを使用しないと言っているのではありません。必ず、プロパティまたはコンストラクタ依存関係の注入を使用してください。インターフェースを使用する必要がありますか?テストオブジェクトをサブクラス化しないのはなぜですか?きれいになりませんか?
フレッド

XML構成/ IoCを行うために、インターフェースはまったく必要ありません。私は、〜8ページと5または6のサービスのみを含む小さなプロジェクトで明確さの大幅な改善を見つけました。サービスとコントローラーがグルーコードを必要としないため、難読化少なくなります。
トーマスW

1

依存関係の構成を一元化して、それらをまとめて簡単に交換できるようにする必要がある場合は、IoCコンテナーが必要になります。これは、多くの依存関係がスワップアウトされるTDDで最も意味がありますが、依存関係間の相互依存関係はほとんどありません。これは、オブジェクト構築の制御フローをある程度難読化するという犠牲を払って行われるため、適切に構成され、合理的に文書化された構成を持つことが重要です。これを行う理由があるのも良いことです。そうでなければ、それは単なる抽象化の金メッキです。私はそれがあまりにもうまくいかなかったので、コンストラクターのgotoステートメントに相当するまで引き下げられました。


1

これが理由です。このプロジェクトはIOC-with-Ninjectと呼ばれています。Visual Studioでダウンロードして実行できます。この例ではNinjectを使用していますが、「新しい」ステートメントはすべて1つの場所にあり、使用するバインドモジュールを変更することで、アプリケーションの実行方法を完全に変更できます。サンプルは、サービスのモックバージョンまたは実際のバージョンにバインドできるように設定されています。重要ではないかもしれない小さなプロジェクトでは、しかし大きなプロジェクトではそれは大きな問題です。

明確にするために、私がそれらを見るときの利点は次のとおりです。1)コードのルートの1つの場所にあるすべての 新しいステートメント。2)1つの変更でコードを完全にリファクタリングします。3)「クールファクター」の余分なポイント「原因は...まあ:クールです。:p


1

私の視点から、なぜIOCが良くないのかを探っていきます。

他のすべてと同様に、IOCコンテナー(またはEinsteinがI = OC ^ 2とするように)は、コードで必要かどうかを自分で決める必要がある概念です。IOCについての最近の流行の叫びはそれだけです、ファッション。ファッションに陥らないでください、それが最初です。コードに実装できる概念は無数にあります。まず、プログラミングを始めてから依存性注入を使用していて、その名前で普及したときにその用語自体を学びました。依存関係の制御は非常に古い主題であり、何から何を切り離しているかに応じて、これまで何兆もの方法で対処されていました。すべてのものからすべてのものを切り離すことはナンセンスです。IOCコンテナーの問題は、Entity FrameworkまたはNHibernateと同じくらい有用であろうとすることです。オブジェクトリレーショナルマッパーを作成することは、システムとデータベースを結合する必要があるとすぐに必要ですが、IOCコンテナーは必ずしも必要ではありません。したがって、IOCコンテナが役立つ場合:

  1. 整理したい多くの依存関係がある状況がある場合
  2. コードをサードパーティ製品と結合する必要がない場合
  3. 開発者が新しいツールを使用する方法を学びたいとき

1:コードに多くの依存関係があること、または設計の早い段階でそれらに気づいていることはそれほど多くありません。抽象的思考が必要な場合、抽象的思考は有用です。

2:コードをサードパーティのコードと結合することはHuGeの問題です。私は10年以上前のコードで作業していましたが、その当時はATL、COM、COM +などの空想的で高度な概念に従っていました。現在、そのコードでできることは何もありません。私が言っていることは、高度なコンセプトは明らかな利点を提供しますが、これは古い利点自体により長期的には取り消されます。それだけですべてがより高価になっただけです。

3:ソフトウェア開発は十分に困難です。高度な概念をコードに取り入れることができれば、認識できないレベルに拡張することができます。IOC2に問題があります。これは依存関係を分離していますが、ロジックフローも分離しています。バグを発見し、状況を調査するために休憩を設定する必要があるとします。IOC2は、他の高度な概念と同様に、それをさらに難しくしています。概念内のバグを修正することは、単純なコードのバグを修正するよりも困難です。なぜなら、バグを修正するときには、概念に従う必要があるからです。(例を挙げれば、C ++ .NETは常に構文を変更しているため、古いバージョンの.NETをリファクタリングする前によく考える必要があります。)では、IOCの問題は何ですか。問題は依存関係の解決です。解決のロジックは、一般的にIOC2自体に隠されています。おそらくあなたが学び、維持する必要がある珍しい方法で書かれています。サードパーティ製品は5年後に存在しますか?マイクロソフト社はそうではなかった。

「私たちはどのように知っているか」という症候群は、IOC2に関するあらゆる場所に書かれています。これは自動化テストに似ています。凝った用語と完璧なソリューションが一目でわかります。すべてのテストを一晩実行して、朝に結果を確認するだけです。自動テストが実際に意味することを会社ごとに説明するのは本当に痛いです。自動テストは、製品の品質を向上させるために夜間に導入できるバグの数を減らす迅速な方法ではありません。しかし、ファッションはその概念を不愉快に支配的にしています。IOC2も同じ症候群を患っています。あなたのソフトウェアが良くなるためにはあなたがそれを実装する必要があると信じられています。EvErYの最近のインタビューIOC2と自動化を実装するかどうか尋ねられました。それは流行のしるしです。会社にはMFCで書かれたコードの一部があり、それらは放棄しません。

ソフトウェアの他の概念としてIOC2を学ぶ必要があります。IOC2を使用する必要があるかどうかの決定は、チームと会社内で行われます。ただし、決定を下す前に、少なくとも上記のすべての引数に言及する必要があります。プラス側がマイナス側を上回っている場合にのみ、ポジティブな判断を下すことができます。

IOC2は、解決する問題のみを解決し、それがもたらす問題を紹介することを除いて、何も問題はありません。他には何もありません。しかし、ファッションに逆らうことは非常に難しく、彼らは汗をかく口を持っています。彼らの空想の問題が明らかになったとき、彼らがどれもそこにいなかったのは奇妙です。ソフトウェア業界の多くの概念は、利益を生み出し、本を書き、会議を開催し、新製品を作ったため、擁護されてきました。それはファッションであり、通常は短命です。人々は何か他のものを見つけたらすぐに完全にそれを放棄します。IOC2は便利ですが、私が見た他の多くの消えた概念と同じ兆候を示しています。それが生き残るかどうかはわかりません。そのためのルールはありません。あなたはそれが有用であるならば、それは生き残ると思います。いいえ、そうはいきません。金持ちの大企業1社で十分であり、このコンセプトは数週間で消滅します。わかります。NHibernateは生き残り、EFは2番目に来ました。多分IOC2も生き残るでしょう。ソフトウェア開発のほとんどの概念は特別なものではないことを忘れないでください。それらは非常に論理的でシンプルで明白であり、概念自体を理解するよりも現在の命名規則を覚えるのが難しい場合があります。IOC2の知識は開発者をより良い開発者にしますか?いいえ、開発者が本質的にIOC2に似た概念を思い付くことができなかった場合、IOC2が解決している問題を開発者が理解することは困難であり、それを使用することは人工的に見え、彼または彼女はそれを使い始めるかもしれませんある種の政治的に正しいことのために。ソフトウェア開発のほとんどの概念は特別なものではないことを忘れないでください。それらは非常に論理的でシンプルで明白であり、概念自体を理解するよりも現在の命名規則を覚えるのが難しい場合があります。IOC2の知識は開発者をより良い開発者にしますか?いいえ、開発者が本質的にIOC2に似た概念を思い付くことができなかった場合、IOC2が解決している問題を開発者が理解することは困難であり、それを使用することは人工的に見え、彼または彼女はそれを使い始めるかもしれませんある種の政治的に正しいことのために。ソフトウェア開発のほとんどの概念は特別なものではないことを忘れないでください。それらは非常に論理的でシンプルで明白であり、概念自体を理解するよりも現在の命名規則を覚えるのが難しい場合があります。IOC2の知識は開発者をより良い開発者にしますか?いいえ、開発者が本質的にIOC2に似た概念を思い付くことができなかった場合、IOC2が解決している問題を開発者が理解することは困難であり、それを使用することは人工的に見え、彼または彼女はそれを使い始めるかもしれませんある種の政治的に正しいことのために。IOC2の知識は開発者をより良い開発者にしますか?いいえ、開発者が本質的にIOC2に似た概念を思い付くことができなかった場合、IOC2が解決している問題を開発者が理解することは困難であり、それを使用することは人工的に見え、彼または彼女はそれを使い始めるかもしれませんある種の政治的に正しいことのために。IOC2の知識は開発者をより良い開発者にしますか?いいえ、開発者が本質的にIOC2に似た概念を思い付くことができなかった場合、IOC2が解決している問題を開発者が理解することは困難であり、それを使用することは人工的に見え、彼または彼女はそれを使い始めるかもしれませんある種の政治的に正しいことのために。


0

個人的には、IoCをアプリケーションのある種の構造マップとして使用しています(そうです、StructureMapも好きです;))。テスト中に、通常のインターフェイス実装をMoq実装に簡単に置き換えることができます。テストセットアップの作成は、IoCフレームワークに新しいinit-callを実行するのと同じくらい簡単で、テスト境界であるクラスをモックで置き換えます。

これはおそらくIoCの目的ではありませんが、私が自分で最も多く使用しているものです。




0

これはかなり古い投稿であることに気づきましたが、それでもまだかなり活発なようで、他の回答でまだ言及されていないいくつかのポイントを投稿したいと思いました。

私は依存性注入の利点に同意すると言いますが、Maxm007の回答で概説されているパターンとは異なるパターンを使用して、自分でオブジェクトを構築および管理することを好みます。サードパーティのコンテナを使用する際に2つの主な問題が見つかりました。

1)サードパーティのライブラリにオブジェクトのライフタイムを「自動的に」管理させると、予期しない結果が生じる可能性があります。特に大規模なプロジェクトでは、オブジェクトのコピーが予想よりもはるかに多くなり、ライフサイクルを手動で管理する場合よりも多くなる可能性があることがわかりました。これは使用するフレームワークによって異なると思いますが、それでも問題は存在します。オブジェクトがリソースやデータ接続などを保持している場合も、オブジェクトが予想よりも長く存続することがあるので、これは問題になる可能性があります。したがって、必然的に、IoCコンテナはアプリケーションのリソース使用率とメモリフットプリントを増加させる傾向があります。

2)私の意見では、IoCコンテナーは「ブラックボックスプログラミング」の一種です。特に、経験の浅い開発者は乱用する傾向があることがわかりました。これにより、プログラマーは、オブジェクトが互いにどのように関連し合うべきか、またはそれらを分離する方法について考える必要がなくなります。これは、オブジェクトを薄い空気から簡単につかむことができるメカニズムを提供するためです。たとえば、ObjectAがObjectBについて直接知ってはいけないという適切な設計上の理由があるかもしれませんが、経験の浅いプログラマーは、ファクトリー、ブリッジ、またはサービスロケーターを作成するのではなく、単に「問題ありません。IoCコンテナーからObjectBを取得するだけです。 」これにより、実際にはオブジェクトの結合が増える可能性があります。これは、IoCが防ぐのに役立つはずです。


-8

ASP.NETプロジェクトの依存性注入は、数行のコードで実現できます。複数のフロントエンドを使用し、ユニットテストが必要なアプリがある場合、コンテナーを使用することにはいくつかの利点があると思います。


1
例を挙げていただけますか?ASP.NETのDIは他のアプリケーションタイプとどのように異なりますか?
tofi9
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.