如何避免dependency injection构造函数的疯狂?

我发现我的构造函数开始看起来像这样:

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

不断增加参数列表。 由于“容器”是我的dependency injection容器,为什么我不能这样做:

 public MyClass(Container con) 

为每个class? 有什么缺点? 如果我这样做,感觉就像我正在使用一个荣耀的静态。 请分享你对IoC和dependency injection疯狂的想法。

你是对的,如果你使用容器作为服务定位器,它或多或less是一个美化的静态工厂。 由于很多原因, 我认为这是一个反模式 。

构造函数注入的一个奇妙的好处是违反单责任原则显而易见。

当发生这种情况时,是时候重构Facade Services了 。 简而言之,创build一个新的,更粗粒度的接口,隐藏您当前需要的一些或所有细粒度依赖之间的交互。

我不认为你的class级build造者应该有你的国际奥委会集装箱期间的参考。 这代表了你的类和容器之间不必要的依赖关系(IOC正试图避免的依赖types)。

传递参数的难度不是问题。 问题是你的class级做得太多了,应该分解得更多。

dependency injection可以作为类的过早预警,特别是因为传递所有依赖的痛苦日益增加。

我遇到了一个关于基于构造函数的dependency injection的类似问题,以及它在所有依赖关系中传递的复杂程度。

过去我使用过的一种方法是使用服务层使用应用外观模式。 这将有一个粗糙的API。 如果这个服务依赖于仓库,它将使用私有属性的setter注入。 这需要创build一个抽象工厂,并将创build存储库的逻辑转移到工厂中。

详细的解释代码可以在这里find

复杂服务层中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; } } 

这是一个简单的方法,如何在注入值之后执行注入和运行构造函数。 这是完全function的程序。

 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

问题:

1)构造函数不断增加参数列表。

2)如果类是inheritance的(例如: RepositoryBase ),那么更改构造函数签名会导致派生类的更改。

解决scheme1

IoC Container传递给构造函数

为什么

  • 没有越来越多的参数列表
  • 构造函数的签名变得简单

为什么不

  • 让你和IoC容器紧密结合。 (这会导致问题,当你想在你使用不同的IoC容器的其他项目中使用这个类时2.你决定改变IoC容器)
  • 使你的课程描述性更低。 (你不能真正看类的构造函数,并说出它需要什么function。)
  • 类可以访问潜在的所有服务。

解决scheme2

创build一个将所有服务分组并将其传递给构造函数的类

  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累积
  • 构造器签名变得简单

为什么不

  • 需要创build额外的类
  • 服务注册变得复杂(您需要分别注册每个X.Dependency
  • 在概念上与传递IoC Container相同
  • ..

解决scheme2只是一个原始的,如果有坚实的论据反对,那么描述性的评论将不胜感激

你使用什么dependency injection框架? 你有尝试过使用基于setter的注射吗?

基于构造函数的注入的好处在于,对于不使用DI框架的Java程序员来说看起来很自然。 你需要5件事来初始化一个类,然后你有5个参数给你的构造函数。 缺点是你已经注意到,当你有很多的依赖关系时,它会变得不方便。

有了Spring,你可以用setter来传递所需的值,你可以使用@required注释来强制注入它们。 缺点是您需要将初始化代码从构造函数移动到另一个方法,并通过@PostConstruct标记,从而调用Spring的所有依赖项。 我不确定其他框架,但我认为他们做了类似的事情。

这两种方式的工作,这是一个优先事项。