首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用依赖项注入注入依赖项注入器

使用依赖项注入注入依赖项注入器
EN

Stack Overflow用户
提问于 2009-12-12 08:50:38
回答 3查看 921关注 0票数 8

我对依赖注入非常陌生,我正在尝试弄清楚这是否是反模式。

假设我有3个程序集:

代码语言:javascript
复制
Foo.Shared - this has all the interfaces
Foo.Users - references Foo.Shared
Foo.Payment - references Foo.Shared

Foo.Users需要一个在Foo.Payment中构建的对象,而Foo.Payment也需要来自Foo.Users的东西。这创建了某种循环依赖关系。

我在Foo.Shared中定义了一个接口,它代理我正在使用的依赖注入框架(在本例中是NInject)。

代码语言:javascript
复制
public interface IDependencyResolver
{
    T Get<T>();
}

在容器应用程序中,我有一个接口的实现:

代码语言:javascript
复制
public class DependencyResolver:IDependencyResolver
{
    private readonly IKernel _kernel;

    public DependencyResolver(IKernel kernel)
    {
        _kernel = kernel;
    }

    public T Get<T>()
    {
        return _kernel.Get<T>();
    }
}

配置如下所示:

代码语言:javascript
复制
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
        ...
    }
}

这允许我从Foo.Users内部实例化Foo.Payment.SomeType的新对象,而不需要直接引用:

代码语言:javascript
复制
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
    }
}

这使得这个实例中UserAccounts类的确切依赖项变得不清楚,这让我认为这不是一个好的实践。

我还能做些什么呢?

有什么想法吗?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2009-12-12 16:21:16

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

这个问题是UserAccounts类的用法变得隐式而不是显式。虽然构造函数声明它需要一个IDependencyResolver,但它并没有声明应该在其中包含什么内容。如果你传递给它一个不能解析ISomeType的IDependencyResolver,它将抛出异常。

更糟糕的是,在以后的迭代中,您可能会尝试从UserAccounts中解析其他类型。它将会编译得很好,但是如果类型不能被解析,它可能会在运行时抛出。

别走那条路。

从给出的信息来看,不可能确切地告诉您应该如何解决循环依赖的特定问题,但我建议您重新考虑您的设计。在许多情况下,循环引用是away 的症状,所以如果你稍微重塑一下你的应用程序接口,它就会消失--通常令人惊讶的是,需要进行如此小的更改。

一般而言,任何问题的解决方案都是添加另一层间接层。如果您确实需要两个库中的对象紧密协作,则通常可以引入一个中间代理。

在许多情况下,Publish/subscribe模型工作得很好。如果通信必须是双向的,

  • 中介器模式可能会提供另一种选择。

  • 您还可以引入抽象工厂<代码>e215,以便在需要时检索所需的实例,而不是要求将其连接到ways
票数 7
EN

Stack Overflow用户

发布于 2009-12-12 11:25:21

我同意ForeverDebugging的观点--消除循环依赖会很好。看看你能不能像这样分离这些类:

只处理支付的

  • Foo.Payment.dll:类,不处理只处理用户的users
  • Foo.Users.dll:类,不处理同时处理支付和用户的payment
  • Foo.UserPayment.dll:类

然后,您有一个引用了另外两个程序集的程序集,但没有依赖项的循环。

如果程序集之间确实存在循环依赖关系,则不一定意味着类之间存在循环依赖关系。例如,假设您有以下依赖关系:

  • Foo.Users.UserAccounts依赖于Foo.Payment.PaymentHistory.
  • A实现的Foo.Shared.IPaymentHistory,不同的支付类Foo.Payment.PaymentGateway依赖于Foo.Shared.IUserAccounts。由Foo.Users.UserAccounts.

实现IUserAccounts

假设没有其他依赖项。

这里有一圈程序集,它们在运行时将在应用程序中相互依赖(尽管它们在编译时不相互依赖,因为它们通过共享的DLL)。但在编译时或运行时,并不存在相互依赖的类的循环。

在这种情况下,您应该仍然能够正常使用IoC容器,而无需添加额外的间接层。在MyModule中,只需将每个接口绑定到适当的具体类型即可。使每个类接受其依赖项作为构造函数的参数。当您的顶级应用程序代码需要一个类的实例时,让它向IoC容器请求该类。让IoC容器负责查找该类所依赖的所有内容。

如果类之间确实存在循环依赖,则可能需要在其中一个类上使用属性注入(也称为setter注入),而不是使用构造函数注入。我不使用Ninject,但它确实支持属性注入- here is the documentation

通常,IoC容器使用构造函数注入-它们将依赖项传递给依赖于它们的类的构造函数。但当存在循环依赖时,这就不起作用了。如果A类和B类相互依赖,则需要将A类的实例传递给B类的构造函数。但是,为了创建A,需要将B类的实例传递给它的构造函数。这是一个先有鸡后有蛋的问题。

通过属性注入,您可以告诉IoC容器首先调用构造函数,然后在构造的对象上设置一个属性。通常,这用于可选依赖项,例如记录器。但您也可以使用它来打破两个相互需要的类之间的循环依赖。

但这并不美观,我绝对建议重构您的类以消除循环依赖。

票数 2
EN

Stack Overflow用户

发布于 2009-12-12 10:26:07

这对我来说确实有点奇怪。有没有可能将需要两个引用的逻辑分离到第三个程序集中,以打破依赖关系并避免风险?

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/1891778

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档