c# - net - di container




如何避免依賴注入構造函數瘋狂? (5)

我發現我的構造函數開始看起來像這樣:

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

不斷增加參數列表。 由於“容器”是我的依賴注入容器,為什麼我不能這樣做:

public MyClass(Container con)

為每個班級? 有什麼缺點? 如果我這樣做,感覺就像我正在使用一個榮耀的靜態。 請分享你對IoC和依賴注入瘋狂的想法。


問題:

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

為什麼

  • 向類中添加新的依賴關係不會影響派生類
  • Class是IoC Container的不可知論者
  • 類是描述性的(在其依賴性方面)。 按照慣例,如果你想知道A類依賴於什麼,那麼這些信息將在A.Dependency累積
  • 構造函數簽名變得簡單

為什麼不

  • 需要創建額外的類
  • 服務註冊變得複雜(您需要分別註冊每個X.Dependency
  • 概念上與傳遞IoC Container相同
  • ..

解決方案2只是一個原始的,如果有堅實的論據反對它,那麼描述性的評論將不勝感激


你使用什麼依賴注入框架? 你有嘗試過使用基於setter的注射嗎?

基於構造函數的注入的好處在於,對於不使用DI框架的Java程序員來說看起來很自然。 你需要5件事來初始化一個類,然後你有5個參數給你的構造函數。 缺點是你已經註意到了,當你有很多依賴關係時,它會變得很難處理。

使用Spring,您可以使用setter傳遞所需的值,您可以使用@required註釋來強制注入它們。 缺點是你需要將初始化代碼從構造函數移動到另一個方法,並通過@PostConstruct標記它來注入所有依賴項後調用Spring。 我不確定其他框架,但我認為他們做了類似的事情。

這兩種方式都有效,這是一種優先選擇。


傳遞參數的難度不是問題。 問題是你的班級做得太多了,應該分解得更多。

依賴注入可以作為類的過早預警,特別是因為傳遞所有依賴關係的痛苦日益增加。


我認為你的班級建造者不應該對你的國際奧委會集裝箱期間有所參考。 這代表了你的類和容器之間不必要的依賴關係(IOC依賴的類型試圖避免!)。


這是我使用的方法

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





ioc-container