8

Generic Classes と Dependency Injection の間に違いはありますか? 制御の反転を実装する方法ではありませんか

ジェネリック クラスは、コンパイル時の安全性が追加された依存性注入を実装する方法ではありませんか?

たとえば、ノード クラスがある場合、次のように定義できます。

class Node<T> where T : ISomeInterface
{
  ..
  ..
}

class Node
{
   ISomeInterface obj
   public Node(ISomeInterface inject)
   {
       obj = inject;
   }
}

更新 2 新しい

class Node<T> where T : ISomeInterface, new()
{
  ISomeInterface obj
  public Node()
  {
     obj = new T();
  }
}

更新 3 @akim : Generics を使用して Generics Repository を使用するように要求された例を作成しました

Interface IRepository 
{   
      public DataTable GetAll();
}

public class ProductRep : IRepository
{ 
       public DataTable GetAll()
       {
            //implementation
       }
}

public class MockProductRep : IRepository
{ 
       public DataTable GetAll()
       {
            //mock implementation
       }
}

public class Product<T> where T : IRepository, new()
{
    IRepository repository = null
     public Product()
     {
       repository = new T();
     }
    public List<Product> GetProduct()
    {
      DataTable prodlst =  repository.GetAll();
      //convert to List of products now
    }
}


//so while using the Product class, client would Supply ProductRep class and in NUnit you //would supply MockProductRep class

Product<ProductRep> obj = new ProductRep<ProductRep>();
List<Product> lst = obj.GetProduct();

//in NUnit
Product<MockProductRep> obj = new ProductRep<MockProductRep>();
List<Product> lst = obj.GetProduct();
4

3 に答える 3

3

それらは同じではありません。ジェネリック型を使用すると、他のさまざまな型に適用できる機能を定義できます。ただし、ジェネリック クラスをインスタンス化すると、コンパイラは、ジェネリック パラメーターとして渡された実際の型への参照を作成します。したがって、宣言は静的であり、コンパイル後に変更することはできません。たとえば、Node クラスをインスタンス化するコードを書くことができます。

Node<SomeImplementation> node1 = new Node<SomeImplementation>();
Node<SomeOtherImplementation> node2 = new Node<SomeOtherImplementation>();

Node クラスをさまざまなシナリオで再利用していますが、アセンブリをコンパイルすると、変数のジェネリック型 (node1 と node2) を変更できません。

一方、依存性注入 (および IoC コンテナー) を使用すると、実行時にアプリの機能を変更できます。依存性注入を使用しISomeInterfaceて、実行時に の 1 つの実装をまったく異なる実装に交換できます。たとえば、2 番目のノード クラスでは、IoC コンテナーを使用して Node クラスを作成できます。次のようなものです。

Node n = Container.Create<Node>();

次に、IoC コンテナーは、構成に基づいて Node クラスをインスタンス化する方法を見つけます。コンストラクターが の実装を必要としていると判断し、実行時ISomeInterfaceに実装を構築する方法を知っています。IoC コンテナーの構成を変更して、同じアセンブリ/コードを実行すると、別の実装が作成され、Node.js のコンストラクターに渡されます。ISomeInterface

これは単体テストで役立ちます。アプリケーションの特定の部分をモックアウトして、特定の 1 つのクラスをテストできるからです。たとえば、通常はデータベースにアクセスするビジネス ロジックをテストしたい場合があります。単体テストでは、データ アクセス ロジックをモックし、特定の各ビジネス ケースをテストするために必要な「静的」データを返す新しい機能を挿入できます。これにより、データベースへのテストの依存関係が解消され、より正確で保守しやすいテストが可能になります。

編集

更新に関しては、パラメーターのないコンストラクターの制限が常に望ましいとは限りません。パラメータを必要とする (自分またはサード パーティによって作成された) クラスがある場合があります。パラメーターなしのコンストラクターを実装するクラスを要求すると、アプリケーションの整合性に影響を与える可能性があります。DI パターンの背後にある考え方は、Node クラスは、クラスが実際にどのように作成されたかを知る必要がないということです。

クラス/依存関係のレイヤーが多数あるとします。ジェネリック型では、次のようになります。

class MyClass<T>
    where T : IUtilityClass
{
    ...
}

class UtilityClass<T> : IUtilityClass
    where T : IAnotherUtilityClass
{
    ...
}

class AnotherUtilityClass : IAnotherUtilityClass
{
    ...
}

この場合、MyClass は UtilityClass を使用し、UtilityClass は AnotherUtilityClass に依存します。したがって、 を宣言するときはMyClass、すべての依存関係を知っておく必要があります... の依存関係だけでなく、MyClassの依存関係もUtilityClass. この宣言は次のようになります。

MyClass<UtilityClass<AnotherUtilityClass>> myTestClass =
    new MyClass<UtilityClass<AnotherUtilityClass>>();

依存関係を追加すると、これは面倒になります。DI を使用すると、IoC コンテナーが自動的にそれらを把握するため、呼び出し元はネストされた依存関係について知る必要がありません。次のようなことをするだけです:

MyClass myTestClass = Container.Create<MyClass>();

MyClass やそのユーティリティ クラスの詳細について知る必要はありません。

通常、IoC コンテナーには他の利点もあります。たとえば、それらの多くはアスペクト指向プログラミングの形式を提供します。また、オブジェクトの有効期間を指定できるため、オブジェクトをシングルトンにすることもできます (1 つのインスタンスのみが作成され、同じインスタンスがすべての呼び出し元に返されます)。

于 2012-07-30T04:28:23.487 に答える
3

ジェネリックは、型パラメーターの概念を導入します。これにより、クラスまたはメソッドが宣言され、コードmsdnによってインスタンス化されるまで、 1 つまたは複数の型の指定を延期するクラスおよびメソッドを設計できます。また、すべての制限とチェックを含むジェネリックは、静的解析を使用してコンパイル時に適用されます

一方、依存性注入は、コンパイル時ではなく実行時 にコンポーネントの選択を可能にするソフトウェア設計パターンです。また、オブジェクト結合は実行時にアセンブラー オブジェクトによってバインドされ、通常、静的分析wikiを使用したコンパイル時には認識されません

質問への回答: 静的分析を使用してコンパイル時に適用されるもの、XML またはコード内構成を使用して実行時に適用されるもの (コンパイルにも有効である必要があります)。バインディングに関する依存性注入の決定は、コンテキストからより多くの情報または構成が利用 可能になるまで延期されます。したがって、ジェネリックと依存性注入は異なり、異なる目的で使用されます。

サンプル #3 の回答

Repository<Entity>さらに一歩進んで、に提供しController、その使用法について考えてみましょう。コントローラーのコンストラクターをどのように実装しますか。

public ControlFreakController<Repository<Entity>>()
{
    this.repository = new Repository<Entity>(); // here is a logical problem
}

また

public ControllerWithInjection(IRepository repository)
{
   this.repository = repository;
}

そして、それが(文字通りハードコードされた)ControlFreakControllerに依存している場合、どのようにテストでカバーしますか?Repository<Entity>デフォルトのコンストラクターがなくRepository<Entity>、独自の依存関係と有効期間がある場合 (たとえば、リポジトリ担当者の HTTP 要求が 1 つだけ存在する必要がある場合) はどうなるでしょうか。翌日、 との作業を監査する必要がある場合はどうなりRepository<Entity>ますか?

于 2012-07-30T04:43:51.460 に答える
0

ジェネリッククラスが次のようになることを意味していると仮定します。

class Node<T> where T : ISomeInterface {
    T obj;

    public Node(T inject) {
        obj = inject;
    }
}

..その場合、依存性注入のためにジェネリック型を開いているだけです(制限付き)。依存性注入の別の「方法」を発見していません - それは依然として依存性注入です。

これは、「現実の」シナリオではあまり役に立ちません。型パラメーターの注入と抑制に基づいて、型パラメーターがどのように使用されるかを仮定しました。また、これには 1 つのタイプのオブジェクトしか注入できませんが、これは非常に悪い仮定です。

new() を使用して更新すると、さらに多くの問題が発生します。注入された型は、パラメーターなしの構築を許可する必要があります。それはあなたをさらに制限します。

于 2012-07-30T04:31:24.963 に答える