web-dev-qa-db-ja.com

直接的なDIコードではなく、なぜIoCコンテナが必要なのですか?

私はしばらくの間 Dependency Injection (DI)を使用して、コンストラクター、プロパティ、またはメソッドのいずれかにインジェクトしています。 コントロールの反転 (IoC)コンテナを使う必要性を感じたことは一度もありません。しかし、私が読むほど、コミュニティからIoCコンテナを使用するように強く圧力を受けています。

StructureMapNInjectUnity 、および Funq のような.NETコンテナで遊んだ。私はまだIoCコンテナが私のコードをどのように恩恵を受ける/改善するのか見ていません。

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

IoCコンテナを使う必要があることを私に納得させてください。仕事で私の仲間の開発者と話すとき、私はこれらの議論を使うつもりです。

598
Vadim

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

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を実装することを望みます。

「いいよ、それほど難しくは聞こえない」ので、あなたは書き始める。

あなたはこれで始めます:

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

..そして最後にthisで終わる:

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;
}

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

その言葉を聞いたことがありますが、それほど難しくはありません。

あなたのチームの何人かの賢い人が思い付いて言ったと想像してみてください。

あなたのプロパティを仮想にする(落ち着いて、それほど大きな問題ではない)なら、そのプロパティの振る舞いを自動的に weave にできます。 (これはAOPと呼ばれますが、名前を気にする必要はありません。それがあなたのために何をしようとしているのかに焦点を当ててください)

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

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

Poof!すべてのマニュアルINotifyPropertyChanged BSは、問題のオブジェクトのすべての仮想プロパティセッターで自動的に生成されます。

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

AOPを行うためのIoCツールの他の興味深い使用法は次のとおりです。

  • 宣言的およびネストされたデータベーストランザクション
  • 宣言的およびネストした作業単位
  • ロギング
  • 事前/事後条件(契約によるデザイン)
441
Ben Scheirman

私はあなたと一緒です、Vadim。 IoCコンテナは、シンプルで洗練された便利なコンセプトを採用しており、200ページのマニュアルで2日間学習する必要があります。

私は個人的には、IoCコミュニティがどのように美しいものを取ったのか Martin Fowlerによるエレガントな記事 そしてそれを典型的には200から300ページのマニュアルを含む複雑なフレームワークの束に変えました。

私は判断しないようにしていますが(HAHA!)、IoCコンテナを使う人は(A)とても頭が良く、(B)頭が良くない人には共感が欠けていると思います。すべてが完璧に理にかなっているので、多くの一般的なプログラマーが概念を混乱させていることを理解するのは困難です。それは 知識の呪い です。 IoCコンテナを理解している人々は、それを理解していない人々がいると信じるのが困難です。

IoCコンテナを使用することの最大の利点は、テストモードと本番モードを切り替えることができるように設定スイッチを1か所に配置できることです。たとえば、データベースアクセスクラスに2つのバージョンがあるとします。1つは積極的にログを記録し、開発中に使用した多くの検証を行ったもの、もう1つはロギングや検証を行わないものです。一箇所でそれらを切り替えることができてうれしいです。一方、これは、IoCコンテナが複雑になることなく、より簡単な方法で簡単に処理されるかなり些細な問題です。

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

138
Joel Spolsky

おそらく誰もあなたにDIコンテナーフレームワークを使わせることを強制していません。クラスを分離してテスト容易性を向上させるためにすでにDIを使用しているので、多くの利点があります。要するに、あなたは単純さを好みます、それは一般に良いことです。

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

依存関係の存続期間管理をもっと細かく制御する必要がある場合(つまり、Singletonパターンを実装する必要があると感じている場合)は、DIコンテナーを調べてください。

DIコンテナを使用する場合は、必要な機能だけを使用してください。 XML構成ファイルをスキップし、それで十分な場合はコードで構成してください。コンストラクタインジェクションに固執する。 UnityまたはStructureMapの基本は、数ページにまとめることができます。

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

37
TrueWill

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.
}
32
bendewey

私の意見では、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クラスを使用した場合は、次のようになります。

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
}

それでは、あなたのStatic 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.
   }
}
32
bendewey

私は宣言的プログラミングのファンです(私が答えるSQL質問の数を見てください)、しかし私が見たIoCコンテナはそれら自身のためにあまりにも難解に思えます。

...あるいは、おそらくIoCコンテナの開発者は明確な文書を書くことができません。

...あるいは他の両方がある程度真実である。

IoCコンテナの 概念 が悪いとは思わない。しかし、その実装は、多種多様なアプリケーションで役立つには十分強力(つまり柔軟)でありながら、シンプルで理解しやすいものでなければなりません。

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

28
Bill Karwin

コンテナーを使用することは、 命令型/スクリプト化 形式の初期化および構成から 宣言型 への変更を主な目的としています。これには、いくつかの異なる効果があります。

  • hairball メインプログラムの起動ルーチンを減らします。
  • かなり深い展開時再構成機能を可能にします。
  • 依存性注入型のスタイルを新作への抵抗の最も少ないパスにする。

もちろん、難しいこともあります。

  • 複雑な起動/シャットダウン/ライフサイクル管理を必要とするコードは、簡単にコンテナに適合させることはできません。
  • おそらく、個人的、プロセス、そしてチームカルチャーの問題をナビゲートする必要があるでしょう。
  • ツールキットの中には急速にヘビー級になっているものもあり、多くのDIコンテナが反発として始めたような深い依存関係を助長しています。
27
Jeffrey Hantin

あなたはすでにあなた自身のIoCコンテナを構築しました (Martin Fowlerによって記述されたさまざまなパターンを使って)そして他の誰かの実装があなたのものより優れている理由を尋ねています。

それで、あなたはすでにうまくいっているたくさんのコードを持っています。そして、なぜそれを他の人の実装に置き換えたいのでしょうか。

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

  • あなたはバグを無料で直します
  • 図書館のデザインはあなたのものより良いかもしれません
  • 人々はすでに特定の図書館に精通しているかもしれません
  • ライブラリはあなたのものより速いかもしれません
  • それはあなたがあなたが実行したがあなたが実行したことを望んでいないいくつかの機能を持っているかもしれません(あなたはサービスロケータを持っていますか?)

短所

  • あなたはバグを、無料で導入されます。
  • 図書館のデザインはあなたのものより悪いかもしれません
  • 新しいAPIを学ぶ必要があります
  • あなたが決して使わないだろう多すぎる機能
  • あなたが書かなかったコードをデバッグするのは通常難しいです
  • 以前のIoCコンテナから移行するのは面倒です。

だから、あなたの短所に対してあなたの長所を比較し、決定を下す。

23
Sam Saffron

IoCの価値の大部分はDIを使用することで得られると思います。あなたはすでにそれをしているので、残りの利益は増加的です。

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

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

  • 多くの拡張性があるため、IoCを使用して構成からコンポーネントを読み込むことができます。これはビルドするのが一般的ですが、ツールはコンテナによって提供されます。

  • 分野横断的な問題でAOPを使用したい場合は、 IoCにメソッド呼び出しを遮断するためのフック があります。これはプロジェクトではあまり一般的ではありませんが、IoCを使用すると簡単になります。

私は以前にこのような機能を書いたことがありますが、これらの機能のうちのどれかが今必要であれば、それが私のアーキテクチャに合っているなら、私はむしろ事前に構築されそしてテストされたツールを使います.

他の人が述べたように、あなたはまたあなたが使いたいクラスを集中的に設定することができます。これは良いことかもしれませんが、それは誤った指示と複雑さを犠牲にして来ます。ほとんどのアプリケーションのコアコンポーネントはそれほど置き換えられていないため、トレードオフはもう少し困難です。

私はIoCコンテナを使用してその機能性を高く評価していますが、トレードオフに気づいたことを認めなければなりません。

17
Steven Lyons

私は回復中の[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コンテナを含めるのは簡単です。

16
Maxm007

あなたがしたい場合はIoCフレームワークは優れています...

  • ...型安全を捨てる。すべてが正しくフックされていることを確認したい場合は、多くの(すべての)IoCフレームワークでコードを実行する必要があります。 「ちょっと、私がこれらすべてのクラスの初期化が本番環境で失敗しないように、すべてが設定されたことを願い、nullポインタ例外をスローします!」

  • ...あなたのコードをグローバルにしてください(IoCフレームワークはグローバルな状態の変更について all です)。

  • 何が何に依存しているのかわからないので、リファクタリングするのが難しい不明瞭な依存関係を持つ巧妙なコードを書いてください。

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-部分は必要ではなく、それを手に入れたならば何かがあなたのデザインに問題があるという警告サインになるはずです。

それを取り除くために(IO部分)、IoCフレームワークのすべての利点を手に入れてください。その代わり、抽象的なファクトリーを使用することもできます。

正しい解決策は次のようになります。

data Foo = Foo { apa :: Bar }

または多分

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

そして注入しなさい(しかし私はそれを注入とは呼ばないであろう)バー。

また、Erik Meijer(LINQの発明者)と一緒にこのビデオを見てください。彼は、DIは数学を知らない人のためのものだと言っています(そして私はもっと同意できませんでした): http://www.youtube.com/watch ?v = 8Mttjyf-8P4

Spolsky氏とは異なり、私はIoCフレームワークを使用する人々が非常に頭が良いとは信じていません - 彼らは数学を知らないと思っています。

14
finnsson

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

私はWeb用にプログラムするのではなく、私のものはコンソールアプリケーションであり、オブジェクトツリーの深部の多くの場所で、ユーザーが指定した設定またはメタデータにアクセスする必要があります。 IoCでは、単にNinjectに設定をシングルトンとして扱うように指示し(常にそれらのインスタンスは1つしかないので)、コンストラクタで設定または辞書を要求し、必要なときに魔法のように表示されます。

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

DI/IoCコンテナには他にも多くの利点があります。他の人がここで詳しく説明し、オブジェクトの作成から要求オブジェクトへの移行は気が遠くなる可能性があります。あなたの武器に!

14
Jeffrey Cameron

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つだけのアプリケーションがある場合、これはそれほど大きな問題ではありません。それにもかかわらず、依存グラフを管理するために何らかの種類のフレームワークを使用しない限り、Dependency Injectionを使用するアプリケーションは、特定のサイズに達すると管理するのが非常に高価になります。

10

あなたが "new"キーワードを使うときはいつでも、あなたは具体的なクラス依存関係を作り出していて、ちょっとした警鐘が頭の中で消えるはずです。このオブジェクトを単独でテストするのは難しくなります。解決策は、そのインタフェースを実装するもの(例えば、モック)でオブジェクトを単体テストできるように、インタフェースにプログラムして依存性を注入することです。

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

同僚がまだDIをしていないのであれば、まずそれに焦点を当てることをお勧めします。簡単にテストできるクリーンなコードを書く方法についてWordを広めてください。クリーンDIコードは難しい部分です。いったんそこに着いたら、オブジェクト配線ロジックをFactoryクラスからIoCコンテナに移行するのは比較的簡単なはずです。

9
matt burns

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

8
bbmud

あなたは 必要ではありません IoCコンテナです。

しかし、DIパターンに厳密に従っているのであれば、1つ持つことで、冗長で退屈なコードを大量に削除できることがわかります。

とにかく、それがライブラリ/フレームワークを使用するのに最適な時期です - それが何をしているのか理解していて、ライブラリなしでそれを実行できる場合。

7
kyoryu

私は、たまたま自家製のDIコードを書き換えて、それをIOCに置き換える過程にあります。私はおそらく200行以上のコードを削除し、それを約10に置き換えました。はい、私はコンテナの使い方を少し学ぶ必要がありました(Winsor) 21世紀なので、私はそれに慣れています。ハウツーを見て私はおそらく約20分を費やした。これは私の時間の価値が十分だった。

6
Matt Wrock

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

IoCコンテナを使用してオブジェクトグラフを構築すると、複雑さが大幅に軽減され、1回限りの構成になります。私は単に「私に「Foo」を作成する」と言って、それを構築するために必要なものを見つけ出します。

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

5
Aaron Lerch

Unityについての議論。大きくなりすぎると、垂木にしわが寄ってくるのが聞こえます。

IoCコードの見た目がきれいであることについて、人々が気付かないうちに私を驚かせることはありません。90年代にC++のテンプレートが優雅な方法で戻ってきた理由について語ったことがあります。 。バー!

4
M Lopez

.NETの世界ではAOPはあまり普及していません。そのため、DIの場合は、自分で作成するのか別のフレームワークを使用するのかにかかわらず、フレームワークが唯一の現実的な選択肢です。

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

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

2
James Black

それで、3年近く経ちましたね。

  1. IoCフレームワークを支持してコメントした人の50%は、IoCとIoCフレームワークの違いを理解していません。私は彼らがあなたがアプリケーションサーバーにデプロイされなくてもコードを書くことができることを知っているかどうか

  2. 最も人気のあるJava Springフレームワークを考えると、それはXMlからコードに移動されたIoC設定であり、このようになります。

`@ConfigurationパブリッククラスAppConfig {

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

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

そして、これを実行するためのフレームワークが必要です。

2
Ivan

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

明確にするために、私がそれらを見るような利点:1)ALL new ステートメントをコードの根本にある一箇所に。 2)1回の変更で完全にコードをリファクタリングします。 3)「クールファクター」の追加ポイントは、それが原因です...まあ:クール。 :P

1
CodeChops

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

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

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

とにかく、インターフェイスは継承よりも優れていると誰が言ったのでしょうか?サービスをテストしているとします。コンストラクターDIを使用して、継承を使用して依存関係のモックを作成してみませんか?私が使用するほとんどのサービスには、いくつかの依存関係しかありません。この方法で単体テストを行うと、大量の無駄なインターフェイスを維持できなくなり、メソッドの宣言をすばやく見つけるためにResharperを使用するhaveが不要になります。

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

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

誰もこれについて考えていますか?

1
Fred

依存関係を一括して簡単に交換できるように、依存関係の設定を集中管理する必要がある場合は、IoCコンテナが必要です。これはTDDで最も理にかなっています。そこでは、多くの依存関係が交換されますが、依存関係の間にはほとんど相互依存関係がありません。これは、オブジェクト構築の制御の流れをある程度分かりにくくすることを犠牲にして行われるため、よく整理され、合理的に文書化された構成を持つことが重要です。これをやる理由があるのも良いことですが、そうでなければ単なる 抽象化金メッキ です。私はそれがあまりにも貧弱であることを見てきたので、それはコンストラクターのためのgotoステートメントに相当するものに引き下げられました。

1
DaveMorganTexas

私の観点からは、なぜIOCが役に立たないのかを探ります。

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

  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がどの問題を解決しているのか理解するのが難しいため、それを使用することは人工的に見えます。ある種の政治的に正しいということのために。

1
user1228608

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

これはおそらくIoCが存在するものではありませんが、私は自分が最もそれを使用しているのがわかります。

0
cwap

IOCコンテナはあなたが持っていないかもしれない問題を解決しますしかしそれは持っているのに良い問題です

http://kozmic.net/2012/10/23/ioc-container-solves-a-problem-you-might-not-have-but-its-a-Nice-problem-to-have/ /

0

私はこれがかなり古い投稿であることを認識していますが、それでもまだかなり活動的であるように思われます、そして私は私がまだ他の答えで言及されていないいくつかの点を貢献すると思いました。

私は依存性注入の利点に同意すると言うつもりですが、私は彼の答えでMaxm007によって概説されたものと違わないパターンを使って自分自身でオブジェクトを構築し管理することを好みます。私はサードパーティ製のコンテナを使用する上で2つの大きな問題を発見しました。

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

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

0
amnesia

依存性注入を実現するためのフレームワークは必要ありません。これは、コアJavaの概念によっても可能です。 http://en.wikipedia.org/wiki/Dependency_injection#Code_illustration_using_Java