web-dev-qa-db-ja.com

Dependency Injectionコンストラクタの狂気を避ける方法は?

私のコンストラクタは次のように見え始めています。

public MyClass(Container con, SomeClass1 obj1, SomeClass2, obj2.... )

パラメーターリストが増え続けています。 「Container」は私の依存性注入コンテナーなので、なぜこれを実行できないのですか:

public MyClass(Container con)

クラスごとに?欠点は何ですか?これを行うと、栄光の静電気を使用しているように感じます。 IoCとDependency Injectionの狂気についての考えを共有してください。

282
JP Richardson

コンテナをサービスロケーターとして使用する場合、それは多かれ少なかれ見栄えの良い静的ファクトリーであるということは正しいです。多くの理由で 私はこれをアンチパターンだと考えています

コンストラクターインジェクションのすばらしい利点の1つは、 単一責任原則 の違反を明白に明らかにすることです。

それが起こったら、 Facade Servicesへのリファクタリング にします。要するに、現在必要なきめ細かな依存関係の一部またはすべての間の相互作用を隠す、新しいcoarse-grainedインターフェイスを作成します。

386
Mark Seemann

クラスコンストラクターには、IOCコンテナー期間への参照が必要だとは思いません。これは、クラスとコンテナの間の不要な依存関係を表します(依存関係のタイプIOCは回避しようとしています!)。

60
derivation

パラメータを渡すことの難しさは問題ではありません。問題は、クラスがやりすぎであり、さらに分解する必要があるということです。

依存性注入は、特にすべての依存性を渡すことの苦痛が増しているため、クラスが大きくなりすぎる場合の早期警告として機能します。

25
kyoryu

コンストラクターベースの依存関係の注入と、それがすべての依存関係を渡すことの複雑さについて、同様の質問に出会いました。

過去に使用したアプローチの1つは、サービスレイヤーを使用してアプリケーションファサードパターンを使用することです。これには粗いAPIがあります。このサービスがリポジトリに依存している場合、プライベートプロパティのセッターインジェクションを使用します。これには、抽象ファクトリーを作成し、リポジトリーを作成するロジックをファクトリーに移動する必要があります。

詳細なコードと説明はこちらにあります

複雑なサービスレイヤーでのIoCのベストプラクティス

3
samsur

コンテナの挿入は、最終的に後悔するショートカットです。

過剰注入は問題ではなく、通常は他の構造的な欠陥の症状であり、最も顕著なのは懸念の分離です。これは1つの問題ではありませんが、多くのソースがある可能性があり、これを修正するのが非常に難しいのは、すべて同時に対処する必要があることです(スパゲッティのもつれを解く)。

ここに注意すべきことの不完全なリストがあります

不十分なドメイン設計(集合ルートの…など)

懸念事項の不十分な分離(サービス構成、コマンド、クエリ)CQRSおよびイベントソーシングを参照してください。

またはマッパー(注意してください、これらのことがトラブルにつながる可能性があります)

モデルやその他のDTOを表示します(再利用しないで、最小限に抑えてください!!!!)

2
Marius

私はこのスレッド全体を2回読みましたが、人々は尋ねられたものではなく、知っていることで答えていると思います。

JPの元々の質問は、リゾルバとクラスの束を送信してオブジェクトを構築しているように見えますが、それらのクラス/オブジェクト自体がサービスであり、注入の準備ができていると想定しています。そうでない場合はどうなりますか?

JP、DIandを活用してインジェクションをコンテキストデータと混合することを望んでいる場合、これらのパターン(または「アンチパターン")具体的に対処します。実際には、そのような努力をサポートするパッケージを使用することになります。

Container.GetSevice<MyClass>(someObject1, someObject2)

...この形式はほとんどサポートされていません。このようなサポートのプログラミングの難しさは、実装に伴う悲惨なパフォーマンスに加えられ、オープンソース開発者にとって魅力的ではないと考えています。

しかし、MyClassのファクトリを作成して登録できるはずであり、そのファクトリは、単に渡すためだけに「サービス」にプッシュされないデータ/入力を受信できるはずなので、実行する必要があります。データ。 「アンチパターン」がネガティブな結果に関するものである場合、データ/モデルを渡すための人工的なサービスタイプの存在を強制することは確かにネガティブです(クラスをコンテナにラップするという感覚と同じです。同じ本能が適用されます)。

ただし、少しいように見える場合でも役立つフレームワークがあります。たとえば、Ninject:

コンストラクターで追加パラメーターを使用してNinjectを使用してインスタンスを作成

これは.NET向けであり、人気があり、今までのようにきれいではありませんが、採用する言語に関係なく何かがあると確信しています。

2
Craig Brunetti

問題:

1)増え続けるパラメーターリストを持つコンストラクター。

2)クラスが継承される場合(例:RepositoryBase)、コンストラクターのシグネチャを変更すると、派生クラスが変更されます。

ソリューション1

IoC Containerをコンストラクターに渡す

理由

  • パラメーターリストが増え続けることはありません
  • コンストラクターの署名がシンプルになります

なぜない

  • IoCコンテナに密結合したクラスを作成します。 (異なるIoCコンテナを使用する他のプロジェクトでそのクラスを使用したい場合に問題が発生します。2. IoCコンテナを変更することにした場合)
  • クラスの説明が少なくなります。 (クラスコンストラクターを実際に見て、機能するために必要なものを言うことはできません。)
  • クラスは潜在的にすべてのサービスにアクセスできます。

ソリューション2

すべてのサービスをグループ化するクラスを作成し、コンストラクターに渡します

 public abstract class EFRepositoryBase 
 {
    public class Dependency
    {
        public DbContext DbContext { get; }
        public IAuditFactory AuditFactory { get; }

         public Dependency(
            DbContext dbContext,
            IAuditFactory auditFactory)
        {
            DbContext = dbContext;
            AuditFactory = auditFactory;
        }
    }

    protected readonly DbContext DbContext;        
    protected readonly IJobariaAuditFactory auditFactory;

    protected EFRepositoryBase(Dependency dependency)
    {
        DbContext = dependency.DbContext;
        auditFactory= dependency.JobariaAuditFactory;
    }
  }

派生クラス

  public class ApplicationEfRepository : EFRepositoryBase      
  {
     public new class Dependency : EFRepositoryBase.Dependency
     {
         public IConcreteDependency ConcreteDependency { get; }

         public Dependency(
            DbContext dbContext,
            IAuditFactory auditFactory,
            IConcreteDependency concreteDependency)
        {
            DbContext = dbContext;
            AuditFactory = auditFactory;
            ConcreteDependency = concreteDependency;
        }
     }

      IConcreteDependency _concreteDependency;

      public ApplicationEfRepository(
          Dependency dependency)
          : base(dependency)
      { 
        _concreteDependency = dependency.ConcreteDependency;
      }
   }

理由

  • クラスに新しい依存関係を追加しても、派生クラスには影響しません
  • クラスはIoCコンテナーに依存しない
  • クラスは記述的です(依存関係の面で)。慣例により、Aが依存するクラスを知りたい場合、その情報はA.Dependencyに蓄積されます
  • コンストラクターの署名が簡単になります

なぜない

  • 追加のクラスを作成する必要があります
  • サービスの登録は複雑になります(すべてのX.Dependencyを個別に登録する必要があります)
  • 概念的にIoC Containerを渡すのと同じ
  • ..

ただし、ソリューション2は単なる生で、それに対する確固たる議論があれば、説明的なコメントをいただければ幸いです

1
tchelidze

これは私が使用するアプローチです

public class Hero
{

    [Inject]
    private IInventory Inventory { get; set; }

    [Inject]
    private IArmour Armour { get; set; }

    [Inject]
    protected IWeapon Weapon { get; set; }

    [Inject]
    private IAction Jump { get; set; }

    [Inject]
    private IInstanceProvider InstanceProvider { get; set; }


}

以下は、値を注入した後に注入を実行し、コンストラクターを実行する方法の大まかなアプローチです。これは完全に機能するプログラムです。

public class InjectAttribute : Attribute
{

}


public class TestClass
{
    [Inject]
    private SomeDependency sd { get; set; }

    public TestClass()
    {
        Console.WriteLine("ctor");
        Console.WriteLine(sd);
    }
}

public class SomeDependency
{

}


class Program
{
    static void Main(string[] args)
    {
        object tc = FormatterServices.GetUninitializedObject(typeof(TestClass));

        // Get all properties with inject tag
        List<PropertyInfo> pi = typeof(TestClass)
            .GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public)
            .Where(info => info.GetCustomAttributes(typeof(InjectAttribute), false).Length > 0).ToList();

        // We now happen to know there's only one dependency so we take a shortcut just for the sake of this example and just set value to it without inspecting it
        pi[0].SetValue(tc, new SomeDependency(), null);


        // Find the right constructor and Invoke it. 
        ConstructorInfo ci = typeof(TestClass).GetConstructors()[0];
        ci.Invoke(tc, null);

    }
}

私は現在、このように機能する趣味のプロジェクトに取り組んでいます https://github.com/Jokine/ToolProject/tree/Core

0
Ronijo