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);
}
}
9 回答
您是对的,如果您将容器用作服务定位器,它或多或少是一个美化的静态工厂 . 由于很多原因I consider this an anti-pattern .
构造函数注入的一个很好的好处是它违反了Single Responsibility Principle的明显 .
当发生这种情况时,是时候refactor to Facade Services了 . 简而言之,创建一个新的更多 coarse-grained 接口,隐藏您当前所需的部分或全部细粒度依赖项之间的交互 .
我不认为你的类构造函数应该引用你的IOC容器周期 . 这表示您的类和容器之间不必要的依赖关系(IOC试图避免的依赖类型!) .
传递参数的难度不是问题 . 问题是你的 class 做得太多了,应该分解得更多 .
依赖注入可以作为类太大的早期警告,特别是因为传递所有依赖项的痛苦越来越大 .
我遇到了一个关于基于构造函数的依赖注入的类似问题,以及它传递所有依赖项的复杂程度 .
我过去使用的方法之一是使用服务层使用应用程序外观模式 . 这将有一个粗糙的API . 如果此服务依赖于存储库,则它将使用私有属性的setter注入 . 这需要创建一个抽象工厂并将创建存储库的逻辑移动到工厂中 .
详细的代码和解释可以在这里找到
Best practices for IoC in complex service layer
问题:
1)具有不断增加的参数列表的构造函数 .
2)如果继承了类(例如:
RepositoryBase
),那么更改构造函数签名会导致派生类发生更改 .解决方案1
将
IoC Container
传递给构造函数为什么
不再增加参数列表
构造函数的签名变得简单
为什么不
使您的课程与IoC容器紧密耦合 . (这会导致问题1.您希望在使用不同IoC容器的其他项目中使用该类.2 . 您决定更改IoC容器)
使您的课程描述性降低 . (您无法真正查看类构造函数并说出它运行所需的内容 . )
类可以访问可能的所有服务 .
解决方案2
创建一个对所有服务进行分组并将其传递给构造函数的类
派生班
为什么
向类添加新依赖项不会影响派生类
类与IoC Container无关
类是描述性的(在其依赖性方面) . 按照惯例,如果您想知道
A
取决于哪个类,那么该信息将在A.Dependency
中累积构造函数签名变得简单
为什么不
需要创建额外的类
服务注册变得复杂(您需要分别注册每个
X.Dependency
)在概念上与传递
IoC Container
相同..
解决方案2只是一个原始的,如果有坚实的论据反对它,那么描述性评论将不胜感激
这是我使用的方法
这是一个粗略的方法,如何在注入值后执行注入和运行构造函数 . 这是功能齐全的计划 .
我目前正在开发一个像这样工作的爱好项目https://github.com/Jokine/ToolProject/tree/Core
我读过这整个帖子,两次,我认为人们正在回答他们所知道的,而不是被问到的 .
JP的原始问题看起来像是通过发送一个解析器来构造对象,然后是一堆类,但我们假设这些类/对象本身就是服务,注入成熟 . 如果他们不是?
JP,如果你想利用DI并希望将注入与上下文数据混合的荣耀,这些模式中没有一个(或假设"anti-patterns")专门解决这个问题 . 它实际上归结为使用一个能够在这样的努力中支持你的包 .
...很少支持这种格式 . 我认为编程这种支持的难度增加了与实现相关的可悲性能,这使得它对开源开发人员来说没有吸引力 .
但它应该完成,因为我应该能够为MyClass'es创建和注册工厂,并且该工厂应该能够接收未被推送为“服务”的数据/输入,仅仅是为了传递数据 . 如果“反模式”是关于消极后果,则强制存在人工服务类型以传递数据/模型肯定是消极的(与你将你的课程包装成容器的感觉相同 . 同样的本能适用) .
但是,有一些框架可能有所帮助,即使它们看起来有点难看 . 例如,Ninject:
Creating an instance using Ninject with additional parameters in the constructor
这是针对.NET的,很流行,并且仍然没有它应该的那么干净,但我确信你选择使用的语言是什么 .
注入容器是您最终会后悔的捷径 .
过度注射不是问题,它通常是其他结构缺陷的症状,最明显的是关注点的分离 . 这不是一个问题,但可以有很多来源,这使得如此难以解决的问题是你将不得不同时处理所有这些问题(想想解开意大利面) .
以下是需要注意的事项的不完整列表
糟糕的域设计(聚合根......等)
关注点分离不良(服务组合,命令,查询)请参阅CQRS和事件源 .
或者Mappers(小心,这些东西会让你陷入困境)
查看模型和其他DTO(永远不要重用一个,并尽量保持最小!!!!)
你使用什么依赖注入框架?您是否尝试过使用基于setter的注射?
基于构造函数的注入的好处是对于不使用DI框架的Java程序员来说看起来很自然 . 初始化一个类需要5件事,然后你的构造函数有5个参数 . 缺点是你注意到了,当你有很多依赖时,它变得笨拙 .
使用Spring,您可以使用setter传递所需的值,并且可以使用@required注释来强制注入它们 . 缺点是您需要将初始化代码从构造函数移动到另一个方法,然后通过使用@PostConstruct标记它来注入所有依赖项之后进行Spring调用 . 我不确定其他框架,但我认为他们做了类似的事情 .
两种方式都有效,这是一个偏好问题 .