仍然需要帮助理解为什么Ninject可能比手动DI更好

时间:2011-04-06 12:16:43

标签: c# ioc-container ninject

这是问题Why do I need an IoC container as opposed to straightforward DI code?

的扩展

我一直在学习Ninject,并提出了以下示例,该示例通过手动方式进行DI和Ninject做DI的方式:

class Program
{
    static void Main(string[] args)
    {
        NinjectWay();
        ManualWay();
        Console.ReadKey();
    }

    private static void ManualWay()
    {
        Console.WriteLine("ManualWay***********************");
        IWeapon sword = new Sword();
        Samurai samurai = new Samurai(sword);

        Console.WriteLine(samurai.Attack("ManualWay..."));

        // change weapon
        IWeapon dagger = new Dagger();
        samurai.Weapon = dagger;

        Console.WriteLine(samurai.Attack("ManualWay..."));

        IWeapon weapon = new Shuriken();
        IWarrior ninja = new Ninja(weapon);
        Console.WriteLine("Manual way.. inject shuriken when a ninja. " + ninja.Weapon.Name);

        IWarrior ninja2 = new Ninja(weapon);
    }

    private static void NinjectWay()
    {
        Console.WriteLine("NinjectWay***********************");
        IKernel kernel = new StandardKernel();
        kernel.Bind<IWeapon>().To<Sword>();
        var samurai = kernel.Get<Samurai>();

        Console.WriteLine(samurai.Attack("NinjectWay..."));

        kernel.Rebind<IWeapon>().To<Dagger>();
        samurai = kernel.Get<Samurai>();

        Console.WriteLine(samurai.Attack("NinjectWay..."));

        kernel.Bind<IWeapon>().To<Shuriken>().WhenInjectedInto<Ninja>();
        var ninja = kernel.Get<Ninja>();
        ninja.OffHandWeapon = new ShortSword();

        Console.WriteLine("Conditional Injection..."+ninja.Weapon.Name);
        Console.WriteLine("Conditional Injection: OffhandWeapon = " + ninja.OffHandWeapon.Name);

        var ninja2 = kernel.Get<Ninja>();
        Console.WriteLine("Conditional Injection..." + ninja2.Weapon.Name);
        Console.WriteLine("Conditional Injection: OffhandWeapon = " + ninja2.OffHandWeapon.Name);

        Console.WriteLine("");
    }
}

我听说当项目规模增加时会带来好处,但我没有看到它。帮助我更好地理解这一点。在C#/ Ninject中提供更多示例,帮助我了解实际上显而易见的好处。

4 个答案:

答案 0 :(得分:6)

与其他答案不同,Ninject主要不是让您的代码更易于测试。这是依赖注入,使您的代码更可测试!通过在facotries中创建所有内容,可以在没有IoC容器的情况下使用依赖注入。但可以肯定的是,能够轻松替换某些部件进行集成测试(不要在单元测试中使用Ninject)是一个很好的副作用。

像Ninject这样的IoC容器主要是将您的类集成到一个可用的软件中。在小型项目中,这可以通过一些工厂轻松完成。但随着应用程序的增长,工厂变得越来越复杂。想象一下,有一个应用程序有各种服务,其中一些是重用的,其他的是为每种用途新创建的。一些服务也被几个组件使用。

如果您使用的是IoC容器,则必须定义完全一次如何获取服务实例以及它的生命周期。另一方面,在工厂中,您必须指定如何获得需要实例的每个类的实例(例如,新的MyServiceFactory()。CreateInstance())。此外,您必须手动控制生命周期。

这意味着随着项目的发展,IoC容器的配置与项目规模一起呈线性增长。但另一方面,工厂变得更加流行,就像整个应用程序中使用的服务一样(例如用户身份验证)。

BTW:你的例子不是很好,因为重新绑定不是一个常见的行为。通常,配置仅在应用程序启动时完成一次。

答案 1 :(得分:0)

测试中有一个好处。

您可以在生产代码中将Sword绑定到IWeapon,在测试程序集中将FakeSword绑定到IWeapon。当你需要测试一些依赖于IWeapon的东西但你实际上并不想要真正的剑时,这很有用。

例如,您可以使用IDataContext和DataContext代替Sword和IWeapon。当您需要连接到数据库时,您的生产代码非常棒。但是对于单元测试,您可能不希望出于各种原因(性能,不一致的数据等)实际访问数据库。因此,您需要将FakeDataContext连接到IDataContext。现在,您已经有了一些控制来模拟数据库活动以进行测试。

答案 2 :(得分:0)

很棒的问题!

BIG BIG赢得恕我直言,当您要求实例化的代码不知道或不关心实际实现是什么时。这在模拟中最为明显 - 您的模拟框架可以将Ninject配置为返回模拟Ninja,Sword和Samarai,以完全不同的方式实现您期望的行为。

例如,我有一个repositry层,依赖于IoC来访问数据存储。对于测试,该数据存储是手工构造的对象集合。对于远程访问,数据存储使用Web服务。在本地,它是SQL。存储库只需要从IoC请求IDataStore并获取配置为服务的任何内容。

这有帮助吗?

答案 3 :(得分:0)

Ninject对您手动执行此操作的主要优点是,其他人已为您完成了大量的抽象操作。显然,您可以通过向Ninject编写类似的代码来重现类似的东西(用于基于接口类型创建对象的东西),这将允许您将对象构造与对象逻辑分开,但是Ninject(和其他库)已经准备好了对你来说很多艰苦的工作,除非你要添加一些新东西,为什么你要再创造它呢?

你在问题​​中得到的比较并不能代表我对Ninject的使用方式。您使用与对象相同的方法绑定和重新绑定。我希望容器设置可以在其他地方完成。这允许您更改构造(例如,通过创建模拟对象更容易测试),而无需更改实际使用构造对象的代码。