2011-04-06 55 views
7

好这是一个扩展的问题Why do I need an IoC container as opposed to straightforward DI code?仍然需要帮助理解为什么Ninject可能会比手动DI

我一直在学习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中提供更多示例,并帮助我理解哪些好处真的变得明显。

回答

5

与其他答案不同,建议Ninject不是主要关于使您的代码更易于测试。它是依赖注入,使您的代码更易于测试!依赖注入可以在没有IoC容器的情况下通过创建实例中的所有内容来使用。但确定,beeing能够很容易地替换一些零件集成测试(不要在单元测试中使用Ninject)是一个很好的副作用。

像Ninject这样的IoC容器主要是关于将你的课程放在一起工作的软件。在小型项目中,这可以使用一些工厂轻松完成。但随着应用程序的增长,工厂变得越来越复杂。想象一下,一个具有各种服务的应用程序,其中一些可以被重用,而另一些则是针对每种用法而新创建的。一些服务也被几个组件使用。

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

这意味着随着项目的增长,IoC容器的配置与项目大小一起呈线性增长。但另一方面,工厂更像指数式增长,就像整个应用程序中使用的服务(例如用户身份验证)一样。

顺便说一句:你的例子不是很好,因为重新绑定不是一个普通的操作。通常在应用程序启动时配置只完成一次。

0

测试中有一个好处。

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

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

0

伟大的问题!

BIG BIG赢得恕我直言,当你的代码要求实例化不知道或关心实际的实现是什么。这在嘲弄中最明显 - 你的嘲讽框架可以配置Ninject以完全不同的方式返回嘲讽的忍者,剑和萨马赖的行为你的期望行为。

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

这有帮助吗?

0

Ninject对你手动执行的主要优点是别人为你做了很多抽象工作。很明显,你可以通过向Ninject写入类似的代码来重现类似的事情(用于创建基于接口类型的对象),这将允许您将对象构造与对象逻辑分离开来,但Ninject(和其他库)已经完成了一切对你来说很多辛苦的工作,所以除非你要添加新的东西,为什么你想再次创建它?

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