9

私は現在、IoC コンテナーを使用する利点を学び、DI に慣れようとしています。私は StructureMap を使用し始めました。これは、かなり単純でありながら強力に思えるためです。

これらの概念に対する私の理解が正しいことを確認したいと思います。アプリケーションで次の基本的なクラスを想定してみましょう (簡潔にするために詳細は省略されています)。

public class OrderService : IOrderService
{
    private IOrderRepository _repository;
    private ITaxCalculator _taxCalculator;
    private IShippingCalculator _shippingCalculator;

    public OrderService(IOrderRepository repository, 
        ITaxCalculator taxCalculator, 
        IShippingCalculator shippingCalculator)
    {
        this._repository = repository;
        this._shippingCalculator = shippingCalculator;
        this._taxCalculator = taxCalculator;
    }

    public IOrder Find(int id) { return this._repository.Find(id); }
}

public class OrderRepository : IOrderRepository
{
    public IOrder Find(int id) { // ... }
}

public class StandardShippingCalculator : IShippingCalculator
{
    // ...
}

public class StandardTaxCalculator : ITaxCalculator
{
    private ITaxSpecification _specification;

    public StandardTaxCalculator(ITaxSpecification specification)
    {
        this._specification = specification;
    }
}

まず第一に、Dependency Inversion Principle は、OrderService は「高レベル」モジュールであるため、下位レベルの実装の詳細に依存するべきではなく、それらのクラスへの参照を持ち、それらに要求できるようにする必要があると述べています。消費するコードは、これらの事前構成されたモジュールを作成しそれに渡す責任を負う必要があります。そうですか?したがって、DI はこれらのクラスを疎結合に保ち、その依存関係のメソッドが呼び出される正確な方法を知る必要がないようにします。呼び出されて、必要なことは何でも実行します。OrderService は、リポジトリが XML を照会しているか、NHibernate や EF、さらには生の DataSet を使用しているかを気にしません。リポジトリを呼び出して、ID が 42 の Order を見つけるように指示するだけで、リポジトリは何をすべきかを認識します。

また、IoC コンテナー (この場合は StructureMap) が、これらすべての依存関係を手動で作成して渡すことを強制しないことでメリットがあることも理解しています。たとえば、簡単なアプリの Main メソッドを使用して、上記のコードには次のものがある可能性があります。

static void Main(string[] args)
{
    IOrderService service = new OrderService(
        new OrderRepository(), new StandardShippingService(), 
        new StandardTaxService(new StandardTaxSpecification()));

    IOrder order = service.Find(42);

    // Do something with order...
}

これは、それを設定するためのすべてのニュースで非常に醜いです。変数を作成したとしても、それはまだ醜いです。IoC コンテナーを使用すると、これらすべてを回避できます。StructureMap の場合は次のようになります。

static void Main(string[] args)
{
    ObjectFactory.Initialize(x =>
    {
        x.For<IOrderRepository>().Use<OrderRepository>();
        x.For<IOrderService>().Use<OrderService>();
        x.For<IOrder>().Use<Order>();
        x.For<IShippingCalculator>()
                        .Use<StandardShippingCalculator>();
        x.For<ITaxCalculator>().Use<StandardTaxCalculator>();
        x.For<ITaxSpecification>()
                        .Use<StandardTaxSpecification>();
    });

    IOrderService service =
                ObjectFactory.GetInstance<IOrderService>();
    IOrder order = service.Find(42);
    // do stuff with order...
}

これははるかにクリーンで、保守が容易で、単体テストを書いている場合は、たとえばモックの具象クラスをサブアウトできます。要するに、利点は、特定のクラスが何に依存しているか (つまり、呼び出し元のコード) を気にする必要さえないところまで、すべてをさらに分離できることです。コンテナーを使用してクラスを作成し、実行させることができます。それは問題であり、消費するコードが必要なものだけを知っていることを確認します。たとえば、実際のアプリでは、コントローラーがサービスを呼び出している場合、リポジトリ、電卓、または仕様について知る必要はありません。知る必要があるのは、使用することだけですOrderService で Order を処理します。

この理解は正しいですか?その点については、まだわからないことがいくつかあります。

  1. IoC コンテナーを使用することにした場合、それはアプリケーションのあらゆる場所で使用されることを意図していますか?それは、解決する逆依存関係が多数ある場合のみですか?それともコンシューマーでのみ使用することを意図していますか? たとえば、具体的な実装を使用して Order を新規作成している場合、OrderRepository で。このクラスは、Order を取得するために StructureMap も使用しますか? これはややばかげた質問かもしれませんが、私が見たすべての DI/IoC の例は、消費するクライアント (Web ページなど) での使用にのみ焦点を当てており、他の場所での使用には決して触れていません。それは全か無かのアプローチのようです。IoC コンテナーを使用する場合は、どこでも使用されます。new SomeObject();この場合、基本的に呼び出しを次のように置き換えています。ObjectFactory.GetInstance<ISomeObject>();

  2. DI/IoC を使用する必要があるかどうか、またはそれをモックするようなものを使用する必要があるかどうかにかかわらず、すべてのクラス (もちろん可能であれば) をインターフェイスから派生させることは良いことでしょうか? 組み込みクラスではないすべてのクラスの背後にインターフェースがある多くのコード例を見てきましたが、これを行うことの利点と将来の保証の可能性を見ることができます.TDDまたはBDDに従うことはこれらの方法論を使用すると、通常、クラスのインターフェイスが必要かどうかがわかりますが、TDD であろうとなかろうと、オブジェクトの型を具体的なクラスとして定義するべきではないと感じている多くの人々を見て話しました。それは常にすべきです基になる型としてインターフェイスまたは抽象クラスになります。これは、YAGNI 違反は言うまでもなく、"Needless Complexity" コードの臭いのケースのようです。

4

2 に答える 2

5

あなたの質問はどちらも物議を醸すトピックに関するものですが、私は議論の私の側に加担します.

IoC コンテナーを使用すると決めた場合、それはアプリケーションのあらゆる場所で使用されることを意図しているのでしょうか?逆方向の依存関係を解決する必要がある場所でのみ使用されるのでしょうか?それともコンシューマーでのみ使用されるのでしょうか?

最上位アプリケーション (コンシューマー) は、依存性注入フレームワークについて知る必要がある唯一のコンポーネントです。new各オブジェクトには、そのジョブを実行するために必要なすべての依存関係インスタンスが含まれている必要があるため、コードベース全体を置き換える必要はありません(Miško Hevery の「依存性注入の神話: 参照の受け渡し」は、最終的に私にとってこのホームを動かしたものでした)。

DI/IoC を使用する必要があるかどうか、またはそれをモックするようなものを使用する必要があるかどうかにかかわらず、すべてのクラス (もちろん可能であれば) をインターフェイスから派生させることは良いことですか、悪いことですか?

この質問の残りの部分は、これに対する答えをすでに知っていることを示しています。インターフェイスを構築して、(問題の具象クラスよりも) より適切な抽象化を作成するか、他の値を提供するだけです。

于 2011-01-05T20:16:36.203 に答える
2

私は現在、VisualStudio2010/12 の C#/WinForm で DI/IoC に深く取り組んでいます。私の選択は Castle Windsor だけでなく、StructureMap にも当てはまりますが、どの IoCC を使用するかは重要ではありません。

非常に詳細な回答については、Mark Seemann の「Dependency Injection in .NET」を読むことをお勧めします。.NET で開発していない場合でも、これは良い本です。

ご質問について:

  1. DI CONTAINER は、どこからでも好きな場所から使用できる可能性があるライブラリですが、それは使用すべきという意味ではありません。コンテナの使用を広げてクラスの大部分に浸透させることはできますが、アプリケーションの 1 つの領域にコンテナを集中させる必要があります。

    この場所は COMPOSITION ROOT と呼ばれ、その場所からのみ DI CONTAINER を使用する必要があります。アプリケーションの COMPOSITION ROOT は、アプリケーションを適切に構成できるように、アプリケーションのルートに配置する必要があります。その方法ではオプションが制限されるため、どのモジュールでもクラスを作成しようとしないでください。アプリケーション モジュール内のすべてのクラスは、CONSTRUCTOR INJECTION (または、まれに、Property Injection などの他のパタ​​ーンの 1 つ) を使用し、COMPOSITION ROOT に任せて、アプリケーションのオブジェクト グラフを構成する必要があります。DI CONTAINER 呼び出しは、COMPOSITION ROOT に限定する必要があります。

    COMPOSITION ROOT は、いくつかのクラスに分散できます。これは当然のことです。重要なことは、すべてのクラスが同じモジュール (できればアプリケーション ルート) に含まれていることです。

  2. どこでもインターフェイスを使用する必要はありません。具象クラスを使用することもできます。確かにインターフェイスはより高いレベルの抽象化を提供しますが、プロジェクトで必要かどうかを検討する必要があります。たとえば、アプリケーションのビジネス ロジックでインターフェイスを使用することをお勧めします。

于 2012-08-31T09:30:37.213 に答える