使用依赖注入注入依赖注入器

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这个实例中类的确切依赖性是什么,这让我觉得这不是一个好习惯.

我怎么能做到这一点?

有什么想法吗?

Mar*_*ann 7

虽然有点争议:是的,这是反模式.它被称为服务定位器,虽然有些人认为它是一种合适的设计模式,但我认为它是一种反模式.

这个问题是,例如您的UserAccounts类的使用变得隐含而不是显式.虽然构造函数声明它需要一个IDependencyResolver,但它没有说明它应该包含什么.如果你传递一个无法解析ISomeType的IDependencyResolver,它就会抛出.

更糟糕的是,在以后的迭代中,您可能很想从UserAccounts中解析其他类型.它将编译得很好,但是如果/当类型无法解析时,可能会在运行时抛出.

不要走那条路.

根据给出的信息,我们无法确切地告诉您如何使用循环依赖关系来解决您的特定问题,但我建议您重新考虑您的设计.在许多情况下,循环引用是Leaky Abstractions的一个症状,所以如果你稍微改造一下你的API,它就会消失 - 通常会令人惊讶的是需要小的变化.

通常,任何问题的解决方案是添加另一层间接.如果您真的需要让两个库中的对象紧密协作,您通常可以引入一个中间代理.

  • 在许多情况下,发布/订阅模型运行良好.
  • 该调解模式可以提供选择,如果通信必须是双向的.
  • 您还可以引入一个抽象工厂来根据需要检索所需的实例,而不是要求它立即连接.