我不喜欢也从不喜欢您引用的工厂命名约定 here。在我之前的一个项目中,我们替换了部分工厂扩展来处理属性。例如,您可以有一个 IFactory 和一个方法 Foo Create([Named] name)。我们还有其他属性,例如 [MatchByType] 或 [MatchByName]。我更喜欢这种方法,因为它更冗长,并且需要更少的先前知识来理解正在发生的事情。
我开始喜欢 Autofac 的代理工厂。但是,我最大的担忧与标准的 Ninject 工厂相同:它不是重构安全的,因为它将委托参数名称与构造函数参数名称匹配(因此我们调整 ninject 工厂以默认按类型而不是参数名称进行匹配)。为 Autofac 创建一个工厂扩展不会那么复杂 - 基本上可以从 Ninjects 实现中复制它。
但是,对于工厂来说,最干净的方法可能如下:
public class FooFactory : IFooFactory
{
private readonly IDependency1 dependency1;
private readonly IDependency2 dependency2;
public FooFactory(IDependency1 dependency1, IDependency2 dependency2)
{
this.dependency1 = dependency1;
this.dependency2 = dependency2
}
public IFoo Create(IParameter someParameter)
{
return new Foo(this.dependency1, this.dependency2, someParameter);
}
}
这是重构安全的。此外,如果您使用 FxCop,如果您有未使用的变量,也会收到警告。
但是,您还必须考虑这是否会为注入工厂的依赖项产生正确的“生命周期范围”。保持清洁,这可能会引入额外的工厂。或者,您可以选择在此处允许与 DI 容器更紧密地耦合,以直接从容器中检索所有依赖项。如果您打算改用 Autofac 的 ILifetimescope(或 Owned<T>),那么无论如何您都会与容器紧密耦合。
最后,我个人选择坚持使用 Autofac 的代理工厂并使用广泛的规范测试来确定一切都按预期工作/防止不完整的重命名重构。
编辑:今天“自动生成的工厂”又遇到了另一个问题。也不是第一次了。这是一个参数不匹配的 autofac 委托工厂。我总是觉得如果有未使用的参数,DI 容器不会警告我,这不是很好。通常这意味着代码中存在错误。
所以如果我可以选择,我想要这样的东西:
public IFoo Create(IParameter someParameter)
{
return new Foo(this.dependency1, this.dependency2, someParameter);
}
我认为相当多的 DI 容器已经支持这种语法来选择构造函数。为什么不用于后期分辨率?另一种方法是拆分分辨率并绑定:
Bind<Foo>().ToConstructor(c => new Foo(c.Inject, c.Inject, c.FromParameter);
然后如果工厂缺少参数,它可能会抛出异常。
但它仍然需要某种匹配逻辑,仍然不一定是重构安全的,等等。