我发现我的构造函数开始看起来像这样:
public MyClass(Container con, SomeClass1 obj1, SomeClass2, obj2.... )
随着参数列表不断增加。由于“容器”是我的依赖注入容器,为什么我不能这样做:
public MyClass(Container con)
每节课?有什么缺点?如果我这样做,感觉我正在使用一个美化的静电。请分享您对IoC和依赖注入疯狂的想法。
答案 0 :(得分:376)
如果您将容器用作服务定位器,那么它是正确的,它或多或少是一个美化的静态工厂。由于很多原因I consider this an anti-pattern。
构造函数注入的一个很好的好处是它会明显违反Single Responsibility Principle。
当发生这种情况时,是refactor to Facade Services的时间。简而言之,创建一个新的,更强大的粗粒度界面,该界面隐藏了您当前所需的部分或全部细粒度依赖关系之间的交互。
答案 1 :(得分:58)
我认为您的类构造函数不应该引用您的IOC容器周期。这表示您的类和容器之间不必要的依赖关系(IOC试图避免的依赖类型!)。
答案 2 :(得分:24)
传递参数的难度不是问题。问题是你的班级做得太多了,应该分解得更多。
依赖注入可以作为类太大的早期警告,特别是因为传递所有依赖项的痛苦越来越大。
答案 3 :(得分:3)
我遇到了一个关于基于构造函数的依赖注入的类似问题,以及传递所有依赖项的复杂程度。
我过去使用的方法之一是使用服务层使用应用程序外观模式。这将有一个粗糙的API。如果此服务依赖于存储库,则它将使用私有属性的setter注入。这需要创建一个抽象工厂并将创建存储库的逻辑移动到工厂中。
详细的解释代码可以在这里找到
答案 4 :(得分:2)
注入容器是您最终会后悔的捷径。
过度注入不是问题,通常是其他结构缺陷的症状,最明显的是关注点分离。 这不是一个问题,但是可以有很多来源,而使之如此难以修复的原因是,您将不得不同时处理所有这些问题(考虑一下意大利面条的整理)。
这是要注意的事情的不完整列表
域名设计不佳(汇总了…等)
关注点分离差(服务组合,命令,查询),请参阅CQRS和事件源。
或Mappers(请注意,这些事情可能会导致您遇到麻烦)
查看模型和其他DTO(切勿重复使用,并尽量将其减少到最小!!!)
答案 5 :(得分:1)
问题:
1)具有不断增加的参数列表的构造函数。
2)如果继承了类(例如:RepositoryBase
),则更改构造函数
签名会导致派生类的更改。
解决方案1
将IoC Container
传递给构造函数
为什么
为什么不
解决方案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;
}
}
为什么
A
取决于哪个类,那么该信息会累积在A.Dependency
为什么不
X.Dependency
IoC Container
解决方案2只是一个原始的,如果有坚实的论据反对它,那么描述性评论将不胜感激
答案 6 :(得分:1)
我阅读了整个主题两次,我认为人们是根据他们所知道的来回应,而不是根据所要求的来回应。
JP最初的问题看起来像是他先发送一个解析器,然后再发送一堆类来构造对象,但我们假设这些类/对象本身就是服务,已经可以注入。如果不是,那该怎么办?
JP,如果您希望利用DI 并且希望将注入与上下文数据混合在一起的荣耀,那么这些模式(或所谓的“反模式”)都不会专门解决这个问题。实际上可以归结为使用一个可以为您提供支持的软件包。
Container.GetSevice<MyClass>(someObject1, someObject2)
...很少支持这种格式。我相信对此类支持进行编程的困难,加上与实现相关的糟糕性能,使得它对开源开发人员没有吸引力。
但是应该这样做,因为我应该能够为MyClass创建并注册一个工厂,并且该工厂应该能够接收不仅仅被用于“服务”的数据/输入。为了传递数据。如果“反模式”是负面影响,那么强迫存在用于传递数据/模型的人工服务类型肯定是负面的(与您将类包装到容器中的感觉相同。本能同样适用)。
尽管有些框架看起来有些难看,但有些框架可能会有所帮助。例如,Ninject:
Creating an instance using Ninject with additional parameters in the constructor
这是针对.NET的,很流行,仍然没有应有的清晰程度,但是我敢肯定,无论您选择使用哪种语言,都有一些东西。
答案 7 :(得分:0)
这是我使用的方法
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
答案 8 :(得分:-7)
您使用的依赖注入框架是什么?您是否尝试过使用基于setter的注射?
基于构造函数的注入的好处是对于不使用DI框架的Java程序员来说看起来很自然。你需要5件事来初始化一个类,然后你的构造函数有5个参数。缺点是你已经注意到了,当你有很多依赖时它会变得笨拙。
使用Spring,您可以使用setter传递所需的值,并且可以使用@required注释来强制注入它们。缺点是您需要将初始化代码从构造函数移动到另一个方法,然后通过使用@PostConstruct标记它来注入所有依赖项之后进行Spring调用。我不确定其他框架,但我认为他们做了类似的事情。
这两种方式都有效,这是一种偏好。