有关Unity构造函数注入的建议做法是什么? 从DI的角度来看,接下来的两个例子是更好的实践?有更好的解决方案吗?
(这些例子很简单)
public interface ICircle
{
double Radius{get;set;}
}
Container.RegisterType<ICircle, SmallCircle>("Small");
Container.RegisterType<ICircle, BigCircle>("Big");
public class Bike{
Public Bike([Dependency("Big") ICircle bigCircle, Dependency("Small") ICircle smallCircle) { }
}
或者更强类型的解决方案...
public interface IBigCircle : ICircle
{
// **Empty interface**
}
Container.RegisterType<ICircle, SmallCircle>();
Container.RegisterType<IBigCircle, BigCircle>();
public class Bike{
Public Bike( IBigCircle bigCircle, ICircle smallCircle) { }
}
令我担心的是,在第二个解决方案中,空接口的数量会随着时间的推移而增长。
答案 0 :(得分:0)
在第一种方法中,您的类知道将注入其中的依赖项的名称。所以这段代码:
[Dependency("Big")] ICircle bigCircle
与以下内容没有太大区别:
bigCircle = ServiceLocator.Locate<ICircle>("Big");
这是service locator anti-pattern。
您的类基本上是指导容器注入具有特定名称的依赖项。它涉及组成过程。在进行适当的依赖注入时,类应仅具有被动角色。
您的第二种方法是尝试通过引入另一个代表相同抽象的接口来解决此问题,以取悦容器。如果ICircle
有decorator会发生什么情况,您是否需要为IBigCircle
创建类似的装饰器?
更好的方法是在没有容器的情况下进行依赖注入。这称为Pure DI。有关相关讨论,请参阅this article。当您有多个单一接口的实现时,纯DI特别有用(在许多应用程序中都是如此)。
答案 1 :(得分:0)
这个例子带有一些假设。例如,建议通过读取IBigCircle
半径大于ISmallCircle
的接口名称,但这取决于实现,而不是接口的名称。界面无法保证其名称所暗示的内容。 (就像IEnumerable<string> bigList, IEnumerable<string> smallList
无法真正执行哪个更大。)
如果重要的是一个比另一个大,你可以依赖两个ICircles
并在运行时确定哪个更大。或者你可以有一个IWheels
接口,由两个ICircles
组成,并使用某种工厂从可用的实现中创建可接受的圆圈对。
答案 2 :(得分:0)
IMO,第一个代码示例比第一个更好,因为它没有使用不必要的接口(例如IBigCircle
)混乱设计。但是,这两个例子都不是很好。第一个示例的问题是使用DependencyAttribute
将容器耦合到应用程序,这是应该避免的。
那么,如果这两个例子并不比一个更好的选择更好吗?
以下是一些。第一种方法是使用InjectionFactory
来实例化所需的对象:
IUnityContainer container = new UnityContainer();
container.RegisterType<ICircle, SmallCircle>("Small");
container.RegisterType<ICircle, BigCircle>("Big");
container.RegisterType<Bike>(new InjectionFactory(
c => new Bike(c.Resolve<ICircle>("Big"), c.Resolve<ICircle>("Small"))));
在上面的示例中,容器用于解析&#34; Big&#34;和&#34;小&#34;注册的ICircle实现。另外,因为我们使用new Bike
,我们得到构造函数参数的编译时检查。
另一种类似的方法是使用InjectionConstructor
来指定要注入的对象:
container.RegisterType<Bike>(new InjectionConstructor(
new ResolvedParameter<ICircle>("Big"),
new ResolvedParameter<ICircle>("Small")
));
上面告诉Unity使用带有两个ICircle
参数的构造函数,ResolvedParameter
表示按名称解析特定的ICircle
。
您可以使用的另一种方法(但可能不是第一选择,但为了完整性而包含在此处)是指定在解决时间而不是注册时间所需的类型:
container.Resolve<Bike>(
new ParameterOverride("bigCircle", container.Resolve<ICircle>("Big")),
new ParameterOverride("smallCircle", container.Resolve<ICircle>("Small")));