4

ここでは、Ioc(Inversion of control)という概念について話しています。 私はこの概念をかなり長い間使ってきました。通常、私は自分のコードにIoCを実装するためのDI(サービス・ロケータではなく)アプローチを採用しています。 以下はIoCについての私の理解です。制御の逆転と依存性注入

classAがその内部にclassBのインスタンスを宣言する場合、classAはclassBに依存している場合。それはclassBに依存している、何らかの理由で(私は後でそれに来る)、良くないので、これを修正するためにDIを使用する。そこで、クラスAのコンストラクタで渡されるIClassBというインタフェースがあります(ここでは、コンストラクタインジェクションを取り上げています)。ここでコードが行く:

public class ClassA 
{ 
    IClassB _classBObj = null; 

    public ClassA(IClassB classBObj) 
    { 
     _classBObj = classBObj; 
    } 

    public void UseClassBMethod() 
    { 
     this._classBObj.classBMethod(); 
    } 
} 

public class ClassB:IClassB 
{ 

    public void classBMethod() 
    { 
     //Some task carried out by classB. 
    } 
} 

public interface IClassB 
{ 
    void classBMethod(); 
} 

そして、ここではそれを実行している行かせるコードを行く:

class Program 
{ 
    static void Main(string[] args) 
    { 
     //This code is outside of class A and hence declaring 
     //object of ClassB is not dependency 
     ClassA classA=new ClassA(new ClassB); 
     classA.UseClassBMethod(); 

    } 
} 

私はのIoCとDIの私の理解が正しいかどうかだけを確認するために、上記の例を示しました。間違ったことが見つかったら、私に修正してください。

  1. テスト容易性:確かに正しいの懸念私は理由のIoCを見つけることを試みながら 今、私は2つの重要な理由を見つけました。上記の例では、classBメソッドがSMPTPサーバーまたは一部のWCF/Webserviceを使用すると仮定してテストすることができない場合があります。ただし、インターフェイスIClassBを実装するテストスタブクラスを作成し、テストスタブクラスのインスタンスを渡すことでテストを進めることができます。

  2. 具体的な実装の依存性:これは私が一緒にはできないものです。

public class ClassA 
{ 
    ClassB _classBObj = null; 

    public ClassA() 
    { 
    _classBObj = new ClassB(); 
    } 

    public void UseClassBMethod() 
    { 
     this._classBObj.classBMethod(); 
    } 
} 

どのようにIoCのが原因ClassBのオブジェクトのClassBの方法の変更により生じたいかなる問題を取り除くために役立っている:私は、次のコードでクラスAのコードを変更した場合でも?メソッドのシグネチャに変更があった場合は、IClassBのメソッドシグネチャのインタフェースを変更する必要があります(実際はリファクタリングタスクが増えています)。メソッドの実装の変更が、たとえば追加のロギングタスクを必要とするとすると、変更はClassBコードに制限されます。

私は質問で少しばかげているかもしれませんが、(ユニットテスト容易性を除けば)このアプローチによって恩恵を受けているシナリオを提示することができますか?

お読みいただきありがとうございます。

+0

これらの投稿がお役に立てば幸いです。 [IoC(Inversion of Control)、DI(Dependency Injection)、完全にデカップリングされたレイヤーを使用した3つのタイヤアプリケーションのアイデア](http://rizviof0.wordpress.com/2012/09/06/an-idea-of-a - 3つのタイヤアプリケーション - 制御逆依存性の注入と完全に分離された層の逆転を使用して)[DI(Dependency Injection)を伴うmvc Webアプリケーションで使用される単純なコンテナ](http:// rizviof0 .wordpress.com/2012/09/06/a-simple-container-in-mvc-web-applications /) –

答えて

6

ここでのコアコンセプトは、program against an interfaceです。
あなたのコードは具体的なクラスを知らない。

結果として、コードに影響を与えずに具体的な実装を切り替えることができます。

ClassA classA=new ClassA(new ClassB);ClassA classA=new ClassA(new ClassC);に置き換えて、 の動作が異なる場合があります。それほど最適ではない、または何かをするために何らかのパスワードが必要なことなど

アイデアはClassCClassBも、その後実装インタフェースに付着した場合は、インターフェイスの変更は、もちろん、あなたのコードが影響を受けている場合は、あなたのコード

に そのままClassCを使用するように変更することができるということです。これは避けることはできません。

しかし、あなたが獲得し、あなたのコードに影響を与えずに、具体的な実装を切り替えて変更できることであり、この変更がより頻繁に必要とされており、ニーズ

+0

DIの概念を説明するための+1。追加するだけで、インターフェイスに加えて(抽象的な)基本クラスに対してもプログラムすることができます。このため、_interfaces_の代わりに_abstraction_という用語が使用されることがよくあります。 –

+0

ありがとうございます。 @ user384706。私は "ClassA classA = new ClassA(新しいClassB); ClassA classA =新しいClassA(新しいClassC);"私はすべてが欲しかった。あなたは正しいです。しかし、メソッド名とシグネチャが同じであれば、巨大なリファクタリングタスクが必要になると思いますか? – James

+0

@ James:通常、設計段階では、要求された機能の抽象的な高水準の見解が十分に理解されています。したがって、「インターフェースに対するプログラミング」の概念に基づいたインターフェースと抽象基本クラスは、変更することはほとんどありませんので、コードをリファクタリングする必要があります。さまざまな実装の詳細とニーズは、通常、初期段階では分かりません。変更する可能性があります。例えば。新しいユースケースが出てくる可能性があり、メソッドはユーザー認証を要求する必要があります。具体的なクラスのコード変更は、より頻繁に変更されることが予想され、 – Cratylus

2

に応じて適切に定義されたインタフェースよりもoccuringだと思います場合によっては、インスタンスをコンストラクタに挿入したくない場合もあります。私はしばしば怠惰なオブジェクトや工場の代理人を注入します。なぜ私は本当にあなたがこれをしたいと思うかを説明しようとします。

実際のシナリオの中には、多くのインスタンスをコンストラクタに注入してしまうことがあります。また、アプリケーションのロジックに応じて、オブジェクトのライフサイクルで実際に使用されるのはほんのわずかです。依存関係を使用しない場合でも初期化することがあります。

.NET 4では、Lazyクラスを使用できます。このクラスを使用すると、依存クラスを初めて使用するときにのみインスタンス化できます。依存関係のインスタンス化に時間がかかったり、インスタンスに大量のメモリが必要な場合はクールです。

public class ClassA 
{ 
    private readonly Lazy<IClassB> _lazyClassBObj; 

    public ClassA(Lazy<IClassB> lazyClassBObj) 
    { 
     _lazyClassBObj = lazyClassBObj;   
    } 

    public void UseClassBMethod() 
    { 
     _lazyClassBObj.Value.classBMethod(); 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     ClassA classA = new ClassA (new Lazy<IClassB>(() => new ClassB)); 
     ... 

    } 
} 

もう1つのトリックは、インスタンスの代わりにコンストラクタにファクトリデリゲートを挿入することです。これはLazyソリューションと非常によく似ていますが、いくつかの利点があります。ファクトリデリゲートの注入は、クラスが依存クラスの任意の数の新しいインスタンスを作成できる必要がある場合(ループ内にインスタンスを作成したい場合など)、すばらしいです。この例では、ClassAは、IClassBを実装するオブジェクトを作成できるメソッドへの参照を持ちます。物事をより面白くするために、IClassBを実装するClassBはIClassC依存関係も持っています。

public class ClassA 
{ 
    private readonly Lazy<IClassC> _lazyClassBObj; 
    private readonly Func<IClassC, IClassB> _classBObjFactory; 

    public ClassA(Func<IClassC, IClassB> classBObjFactory, Lazy<IClassC> lazyClassBObj) 
    { 
     _classBObjFactory = classBObjFactory; 
     _lazyClassBObj = lazyClassBObj;   
    } 

    public void UseClassBMethod() 
    { 
     var classC = _lazyClassBObj.Value; 

     var classB1 = _classBObjFactory(classC); // instance 1 
     var classB2 = _classBObjFactory(classC); // instance 2 
     var classB3 = _classBObjFactory(classC); // instance 3 
    } 
} 

このアプローチの主な利点は、使用しない可能性のあるオブジェクトを初期化しないことによってメモリ要件を削減することです。ファクトリデリゲートインジェクションの利点は、依存関係のinitロジックを隠すことができ、呼び出し元に公開されているファクトリパラメータを制御できることです。 ClassAは、すべての依存関係をどのように組み立てるかを知る必要はなく、知っているパラメータだけを知る必要があり、制御することができます。依存関係をより簡単に置き換えることができます。

私はそれが意味を成し遂げたいと思っています:)ちょうどあなたがC#機能スタイルを使ってパターンからもっと多くを得る方法をデモしたいと思っていました。

+0

+1に影響しません。ありがとうございました。私は間違いなくそれらを使用することを楽しみにしています。 – James

+0

この回答は十分な愛を得ていません。私はまた、ポストで説明されたすべての理由のために工場のメソッド代理人を注入することに部分的です。 Jon Skeetの "miscutil"を見ると、彼はまた、いくつかのシナリオでこのアプローチを使用していると思います。 –