and*_*ecu 8 c# design-patterns dependency-injection ninject
对依赖注入很新,我试图弄清楚这是否是反模式.
假设我有3个组件:
Foo.Shared - this has all the interfaces
Foo.Users - references Foo.Shared
Foo.Payment - references Foo.Shared
Run Code Online (Sandbox Code Playgroud)
Foo.Users需要一个在Foo.Payment中构建的对象,而Foo.Payment也需要来自Foo.Users的东西.这会产生某种循环依赖.
我在Foo.Shared中定义了一个接口,代理我正在使用的依赖注入框架(在本例中为NInject).
public interface IDependencyResolver
{
T Get<T>();
}
Run Code Online (Sandbox Code Playgroud)
在容器应用程序中,我有一个这个接口的实现:
public class DependencyResolver:IDependencyResolver
{
private readonly IKernel _kernel;
public DependencyResolver(IKernel kernel)
{
_kernel = kernel;
}
public T Get<T>()
{
return _kernel.Get<T>();
}
}
Run Code Online (Sandbox Code Playgroud)
配置如下所示:
public class MyModule:StandardModule
{
public override void Load()
{
Bind<IDependencyResolver>().To<DependencyResolver>().WithArgument("kernel", Kernel);
Bind<Foo.Shared.ISomeType>().To<Foo.Payment.SomeType>(); // <- binding to different assembly
...
}
}
Run Code Online (Sandbox Code Playgroud)
这允许我Foo.Payment.SomeType从Foo.Users内部实例化一个新对象,而无需直接引用:
public class UserAccounts:IUserAccounts
{
private ISomeType _someType;
public UserAccounts(IDependencyResolver dependencyResolver)
{
_someType = dependencyResolver.Get<ISomeType>(); // <- this essentially creates a new instance of Foo.Payment.SomeType
}
}
Run Code Online (Sandbox Code Playgroud)
这使得我不清楚UserAccounts这个实例中类的确切依赖性是什么,这让我觉得这不是一个好习惯.
我怎么能做到这一点?
有什么想法吗?
虽然有点争议:是的,这是反模式.它被称为服务定位器,虽然有些人认为它是一种合适的设计模式,但我认为它是一种反模式.
这个问题是,例如您的UserAccounts类的使用变得隐含而不是显式.虽然构造函数声明它需要一个IDependencyResolver,但它没有说明它应该包含什么.如果你传递一个无法解析ISomeType的IDependencyResolver,它就会抛出.
更糟糕的是,在以后的迭代中,您可能很想从UserAccounts中解析其他类型.它将编译得很好,但是如果/当类型无法解析时,可能会在运行时抛出.
不要走那条路.
根据给出的信息,我们无法确切地告诉您如何使用循环依赖关系来解决您的特定问题,但我建议您重新考虑您的设计.在许多情况下,循环引用是Leaky Abstractions的一个症状,所以如果你稍微改造一下你的API,它就会消失 - 通常会令人惊讶的是需要小的变化.
通常,任何问题的解决方案是添加另一层间接.如果您真的需要让两个库中的对象紧密协作,您通常可以引入一个中间代理.