【问题标题】:When should I use dependency injectors like Ninject我什么时候应该使用像 Ninject 这样的依赖注入器
【发布时间】:2013-08-26 23:10:41
【问题描述】:

问题

我有一些问题要理解在我的代码中何时何地应该使用像 Ninject 这样的依赖注入器。

代码

假设我们有以下代码:

//WITHOUT NINJECT:     
IMailSender mailSender = new MockMailSender();

//WITH NINJECT:
IMailSender mailSender = kernel.Get<IMailSender>();

这不是依赖注入,那么在这种情况下使用 Ninject 有意义吗?

另一个例子展示了我的代码是如何被依赖注入器弄得一团糟的:

 public void CalculateRevenueRecognitions(IContract contract)
    {
        //WITH NINJECT
        var kernel = new StandardKernel(new DefaultModule());
        var arguments = new List<IParameter>
        {
            new ConstructorArgument("amount",contract.Revenue),
            new ConstructorArgument("date", contract.WhenSigned)
        };
        contract.AddRevenueRecognition(kernel.Get<IRevenueRecognition>(arguments.ToArray()));

        //WITHOUT NINJECT:
        contract.AddRevenueRecognition(new RevenueRecognition(contract.Revenue, contract.WhenSigned))));
    }

问题

什么时候应该使用依赖注入器?

  1. 关于构造函数注入、参数注入等
  2. 关于对象创建(依赖注入器是否将经典对象创建替换为新对象?)
  3. 是其他人吗?

什么时候不应该使用依赖注入器?

【问题讨论】:

  • 在使用 Ninject 参与一个项目并获得一些基本的熟悉之后,测试对我来说变得容易多了。开始使用用例非常困难,您的示例代码不应该使用它。我认为它更多是因为您有一个要使用的查询类,该查询类取决于依赖于设置类的数据库类等。您在 ninject 模块中定义这些绑定,然后请求一个查询对象,然后为您注入其他依赖类。
  • 它绝对适用于这段代码,正如我在下面提到的,它配置在错误的地方。大多数依赖关系可以在应用程序启动时解决。
  • 在业务对象方法中使用 kernel.Get 对我来说似乎不合适。我对ninject还是新手,所以我想我错了。到目前为止,我只需要使用构造函数注入。
  • 我不明白。今天,互联网上的每个人都在写有关 IoC 框架的文章。这不应该是一个容易回答的问题吗?是否有任何规则何时使用它以及何时不应该使用它。我有点迷路了......
  • 我只在我觉得需要它的时候,或者当我的老板强迫我时,才在我的工具集中添加一个工具:-)。恕我直言,如果你真的不知道它能给你带来什么好处,你不应该使用它。

标签: c# dependency-injection dependencies inversion-of-control ninject


【解决方案1】:

我过去曾广泛使用依赖注入框架。天哪,我什至写了一个(它执行注册和解析比 Ninject 快光年,虽然我很高兴借用它有趣的 Ninjas、KiteShields Shuriken 等单元测试模型)

这些天来,我几乎没有使用依赖注入框架。因为如果我从使用(重)框架中学到了一件事,那就是:

您遇到了技术问题。您搜索并尝试可以为您解决问题的框架。您选择一个并在任何地方使用它。

现在你有 2 个问题。您的依赖注入框架只是导致您对整个代码库产生很大的依赖。

Greg Young (http://skillsmatter.com/podcast/agile-testing/simple-is-better) 有一个很棒的 SkillsMatter 演讲,关于框架的陈述和观点可能看起来有点极端,但其中有不少,我是通过艰难的方式学习的,我同意与。

【讨论】:

  • 感谢您分享您对 IoC-Frameworks 的意见,我会看看您的参考资料
  • 我认为大多数人会争辩说,对相对容易交换的东西有一个依赖比在代码中可能存在数千个具体依赖要好。这是一个相当矛盾的观点,而不是被大量开发人员所认同的观点。如果您的代码库正在传递对容器的引用,那么您最好重新考虑您的架构。
  • @MarkWalsh 我并不是说管理你的依赖关系不重要。我的观点是,如果你设计得好,你就不需要一个框架来为你做这件事,因为你最终会不可避免地依赖它,而它的“魔力”会让你付出代价。您是否真的看过我链接到的演示文稿,它向您展示了实现这一目标的一种方法?我也不会被“最争论的东西”所困扰。我发现如果我想用最简洁的方法解决问题而不是寻找“最有争议的”方法,效果会更好。
  • 您的回答听起来好像很难解耦您选择的框架,如果您想更改它。如果您正确设计架构,则不是。我的 Ninject 代码没有污染我的应用程序代码,一切都在运行时解决。如果我想使用 Spring,绑定模块是我唯一需要更改的地方。需要有一条底线,我发现像格雷格这样的纯粹主义者似乎达到了 N 级只是为了证明他们不依赖于 X。
  • 是的,如果您的课程做得太多,那将成为一个问题。再说一次,你的类不应该按照单一责任原则做那么多事情。
【解决方案2】:

基本前提是不依赖具体类(就像你说的新建具体类)并注入实现(通过接口)。这取决于您正在执行的特定任务以及在什么条件下(Windows 服务、WCF 服务、Asp.Net),但在大多数情况下,您有一个包含您希望公开公开的所有方法的接口,就像这样

public interface IBar {
    void DoStuff();
}

然后你使用 Ninject 将它们绑定到一个特定的类,即

 Bind<IBar>().To<BarClass>();

所以在启动时,Ninject 会去获取配置的实现。这样做的好处是您的实现已配置,因此只要它正在实现接口,就很容易切换到另一个实现。因此,如果您现在想使用另一个类而不是 BarClass,则可以重新绑定到它,即

Bind<IBar>().To<NewBarClass>();

所以无论你需要在哪里使用这个 NewBarClass,你都可以像这样传递它

public class UserOfNewBarClass {

public UserOfNewBarClass(IBar newBarClass) {
}
   // Use the IBar interface
}

此外,您可以在测试时模拟接口,这意味着您可以隔离单个具体类并完全隔离地测试它们。您可以做更复杂的事情,稍后可以学习,例如基于属性值的绑定和对注入内容的条件绑定。

有关入口点,请参阅这些

WCF - http://www.aaronstannard.com/post/2011/08/16/dependency-injection-ninject-wcf-service.aspx

MVC - http://www.shahnawazk.com/2010/12/dependency-injection-in-aspnet-mvc-3.html

Windows 服务 - Using Ninject with a Windows Service

起初很难理解,但容器(在本例中为 Ninject)会根据您指定的绑定确定要注入的实现。最终目标是注入您的类所需的一切以执行它的方法,以便您可以单独测试它(它还有助于保持类清洁和整洁)。首选方法是通过构造函数注入,因为类在创建时将具有其所有依赖项。属性注入当然是可行的,但与属性一样,您不能真正保证它已被设置。

【讨论】:

  • 我和你在一起,到目前为止我理解这个概念。但这只有在您的 BarClass 确实是一个像 (var bar = new BarClass()) 这样的新对象时才有效,但是如果该对象很久以前就创建了然后被注入到一个类中会怎样。那么在消费类中使用 Ninject 有意义吗?
  • 自从您发表评论后,我已经更新了,但我会尝试扩展。 Ninject 真的应该在您的应用程序启动之前解决所有问题。您尝试在什么环境中使用它,即什么类型的应用程序?也许我可以更具体一点。
  • 抱歉,Mark 但您只是在重复 Ninject 的用户指南。我的问题不是如何使用 Ninject,而是何时使用以及何时不使用它。不过,我真的很感谢你的努力。
  • 我没有重复,忍者指南使用武器的概念来尝试描述它,这些都是我的话。我会亲自将工厂注入到需要新建新对象的构造函数中。这不会是一个具体的实例,但会是我绑定的一个实现。具体实现将新建对象,即 new SomeConcreteClass();。我的 cmets 还描述了何时、为什么以及如何使用依赖注入。我想不出一个我无法使用依赖注入的实例。
  • 那你就明白了。你在你的业务逻辑中配置东西,而这一切都应该在你的代码到达这一点之前被绑定。
【解决方案3】:

问题是,我现在无法想象我会单独使用依赖注入的场景。我更熟悉使用控制反转(使用 DI 的 IoC 容器)的情况。在这种情况下,您的代码实际上非常干净,因为您不必为实例调用注入器。 IoC 容器将为您完成。

所以我会考虑,如果使用 IoC/DI 而不是仅使用 DI 不是更好。最后,许多成功的框架都使用了这种组合,没有一个只使用其中的一个(假设它们完全使用 DI,因为实际上还有另一种模式可以与 IoC 组合)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-12-06
    • 1970-01-01
    • 1970-01-01
    • 2021-09-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多