【问题标题】:Understanding the benefits of Ninject了解 Ninject 的好处
【发布时间】:2013-09-12 20:01:37
【问题描述】:

我已经看到这个问题Still need help understanding why Ninject might be better than manual DI,但我仍然对 Ninject 的用处感到困惑......

我明白这段代码...

class Samurai
{
    readonly IWeapon weapon;

    [Inject]
    public Samurai(IWeapon weapon)
    {
        this.weapon = weapon;
    }

    public void Attack(string target) 
    {
        this.weapon.Hit(target);
    }
}

... 将产生以下“(基本上)看起来像这样的动态方法:”

delegate(IWeapon weapon)
{
    return new Samurai(weapon);
}

这到底有什么用?如果没有 Ninject,我仍然可以这样做(根据 Ninject 文档“手动依赖注入”-No Ninject):

class Program
{
    public static void Main() 
    {
        var warrior1 = new Samurai(new Shuriken());
        var warrior2 = new Samurai(new Sword());
        warrior1.Attack("the evildoers");
        warrior2.Attack("the evildoers");
    }
}

使用 Ninject 为我提供了哪些仅遵循松散耦合的基本原则我无法做到的事情?感谢您帮助我理解。

【问题讨论】:

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


    【解决方案1】:

    Darin 的回答涵盖了使用 DI 框架最显着的好处,但我发现在使用 DI 容器时有一些帮助。假设您有一个具有这些服务依赖项的对象:

    public class Order : IOrder
    {
        public Order(IOrderService orderSvc, ICustomerService custSvc)
        {
            // constructor logic
        }
    }
    

    您的代码可能如下所示:

    var order = new Order(new OrderService(), new CustomerService()); // manual
    or
    var order = kernel.Resolve<IOrder>(); // using a DI container
    

    这工作得很好。现在,突然之间,您的要求发生了变化。订单现在需要送货服务:

    public Order(IOrderService orderSvc, 
        ICustomerService custSvc, IShippingService svc)
    {
        // constructor logic
    }
    

    假设您在整个程序中有 10 个不同的地方手动创建订单。如果您自己处理注入,则必须在代码中找到所有 10 个您正在创建订单并修改它们的位置。这样做 10 次可能是很多工作!

    var order = new Order(new OrderService(), new CustomerService(), new ShippingService());
    

    但是,使用 DI 容器,一旦您注册了一个绑定,您在所有这 10 个地方的代码如下所示:

    var order = kernel.Resolve<IOrder>();
    

    看看和以前一样吗?你不必改变任何东西!无论您添加 1 个依赖项还是 100 个依赖项,您解决订单的所有这 10 个位置都不会改变**。因此它可以帮助您避免在需要新依赖项时修改现有代码。

    ** 情况并非总是如此,但这是一个简化的示例,展示了没有人谈论太多的额外好处之一。

    【讨论】:

    • 谢谢,这与我所寻找的很接近——DI 框架提供的好处的一个实际例子。但是,每当我实例化一个对象时,它通常会在构造函数中包含特定数据,所以当我需要调用 new Order(myOrder, myCustomerService, myShippingService); 之类的东西时,我看不出调用通用 kernel.Resolve&lt;IOrder&gt;(); 会有什么帮助,对吧?
    • @user1477388 这取决于 DI 框架如何完成。假设 Order 在构造函数中需要一个订单 ID 参数。对于 Ninject,它看起来像这样:kernel.Get&lt;IOrder&gt;(new ConstructorArgument("orderId", orderId)) 或类似的内容。如果您需要提供不可解析的参数,则必须修改您调用kernel.Get&lt;IOrder&gt;() 的所有位置。
    • 那么这是我的想法;如果你有很多地方需要更新到 DI 框架使你受益的地方,这是否意味着你实际上没有遵循 DI 原则?整个概念是一个单一的服务处理一切,您只需订阅该服务,我们的代码库设置为,如果您需要更改所有内容,您只需要更新一个地方,所以对我来说就像您一样'在你的代码之上添加一个完整的层,使其对 DI 友好,以弥补它没有被架构成对 DI 友好。
    【解决方案2】:

    使用 Ninject 为我提供了我无法做到的事情 遵循松散耦合的基本原则?

    • Composition Root 中的代码行更少
    • 用于处理对象生命周期的标准容器
    • 许多插件用于特定上下文中的注入,例如经典 WebForms、ASP.NET MVC、ASP.NET Web API
    • 可以自动处理您的 IDIsposable 对象
    • ...

    否则,您应该手动处理的所有事情。话虽如此,DI框架并不重要。它应该很快,并提供您的应用程序所需的特定功能。但是 DI 框架绝对不应该以松散耦合的方式影响您设计代码和应用程​​序中不同层的方式(针对接口和抽象类进行编程以削弱应用程序不同层之间的耦合)。

    因此,可以将 DI 框架视为仅干预应用程序的组合根的东西,并且可以将其视为一个框架,您可以在眨眼之间将其替换为不同的框架,甚至可以手动处理您的对象生命周期。

    例如,您在问题中显示的代码非常糟糕,因为它将您的层与特定的 DI 框架联系在一起。那边的[Inject] 属性就像癌症一样。这意味着您的应用程序代码依赖于特定的 DI 框架。

    【讨论】:

    • 感谢您的回答。我正在阅读manning.com/seemann/DIi.NET_sample_ch01.pdf 并试图更好地理解。真正对我有帮助的是一些实用的东西,比如“这里有没有 Ninject 的代码”和“这里有没有 Ninject 的更好代码”。我只是看不出这是如何/在哪里有益的......仍在尝试!
    • 您在应用程序的所谓组合根中使用 NiNject(阅读我的答案中的链接以更好地理解组合根是什么)。基本上,像 Ninject 这样的 DI 框架为您提供了一个很好的 DSL 来描述您的对象图依赖关系和生命周期,这可以通过大量手动代码和 new 语句来实现。更别提一辈子了。在复杂的应用程序中,您希望将一些依赖项作为单例,而将其他依赖项作为临时生命周期。还可以利用 DI 框架为您提供的特定扩展。
    • 例如,如果您正在编写一个 ASP.NET MVC 应用程序,那么您可以使用 Ninject.MVC3,它将为您提供一个成熟的 DependencyResolver,能够在各个层中注入依赖项,甚至在动作过滤器中也是如此。
    • 感谢您的帮助。我只是在寻找一个示例,该示例显示“没有像 Ninject 这样的 DI 框架,X [代码示例] 将是不可能的”或“使用像 Ninject 这样的 DI 框架使 X [代码示例] 比 Y 更容易/更清晰[代码示例]。”你懂我的意思吗?只是试图从实际而不是概念上来看待这一点。
    【解决方案3】:

    如果你想测试你的程序怎么办?使用您的手动方法,测试环境现在取决于定义的 Shuriken() 和 Sword()。

    使用像 Ninject 这样的框架,您可以创建应用程序,以便在实际运行应用程序时引入依赖项。这意味着您可以传入模拟数据,因此现在您的测试环境将没有依赖关系。

    【讨论】:

    • 这绝对不是真的。 DI 框架对单元测试完全没有帮助。它与它无关。你可以设计一个完美的单元可测试系统,层之间的耦合很弱,没有任何 DI 框架的痕迹。
    • 此 Wiki 将测试列为 DI 的主要优势:en.wikipedia.org/wiki/Dependency_injection
    猜你喜欢
    • 1970-01-01
    • 2012-11-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多