Ninject模块的目的是什么?

60

我完全不了解ninject

我一直在分析别人的代码,并发现了几个nInject模块 - 这些类派生自Ninject.Modules.Module,并且具有一个包含大部分代码的load方法。

通过调用StandardKernel实例的LoadModule方法并传递模块类的实例来调用这些类。

也许我在这里漏了什么明显的东西,但是相比于只创建一个普通的类并调用其方法,或者创建一个静态的类和静态方法,这有什么好处呢?

 

3个回答

63

Ninject模块是用于向IoC容器注册不同类型的工具。优点在于这些模块会被保存在它们自己的类中,这使得你可以将不同的层次/服务放在它们自己的模块中。

// some method early in your app's life cycle
public Kernel BuildKernel()
{
    var modules = new INinjectModule[] 
    {
        new LinqToSqlDataContextModule(), // just my L2S binding
        new WebModule(),
        new EventRegistrationModule()
    };
    return new StandardKernel(modules);
}

// in LinqToSqlDataContextModule.cs
public class LinqToSqlDataContextModule : NinjectModule
{
    public override void Load()
    {
        Bind<IRepository>().To<LinqToSqlRepository>();
    }
}

拥有多个模块可以实现关注点分离,即使在您的IoC容器内部也是如此。

你的问题似乎更多地涉及IoC和DI作为一个整体,而不仅仅是Ninject。是的,您可以使用静态配置对象来完成几乎所有IoC容器所做的事情。当您拥有多个依赖层次结构时,IoC容器变得非常好用。

public interface IInterfaceA {}
public interface IInterfaceB {}
public interface IInterfaceC {}

public class ClassA : IInterfaceA {}

public class ClassB : IInterfaceB
{
    public ClassB(IInterfaceA a){}
}

public class ClassC : IInterfaceC
{
    public ClassC(IInterfaceB b){}
}

目前构建ClassC很痛苦,因为存在多层接口。向内核请求一个IInterfaceC会更容易。

var newc = ApplicationScope.Kernel.Get<IInterfaceC>();

1
LinqToSqlDataContextModule 应该扩展 StandardModule 吗? - Mark Gibaud
1
@Mark Gibaud在Ninject 2中已经失效。StandardModule已经消失,被NinjectMdule所取代 - http://github.com/enkari/ninject/tree/master/src/Ninject/Modules/ - Jarrett Meyer
1
这是一个很好的例子,但是测试可行性呢?我经常听说 DI/IoC 容器可以帮助我实现测试可行性,但是我很难找到一个真正的例子来展示 Ninject 不会污染我的代码,并且还能促进测试可行性。 有没有一些真实世界的开源项目可以让我参考,以便理解这个概念? - Piotr Owsiak
1
另一个问题是Ninject文档。那些家伙会提供这样的东西吗?Ninject目前处于2.0版本,我甚至找不到1.0版本的可靠文档。 - Piotr Owsiak
2
@Piotr Owsiak 什么是Ninject文档? :) - Jarrett Meyer
显示剩余4条评论

9
也许我错过了一些显而易见的东西,但是相比于创建一个普通的类并调用其方法或者使用静态类和静态方法,这种方法有什么好处呢?
是的,你可以只调用一堆 Bind<X>().To<Z>() 语句来设置绑定,而不需要模块。
区别在于,如果将这些语句放在一个模块中,则:
  • IKernel.Load(IEnumerable<Assembly>) 可以通过反射动态发现这些模块并加载它们。
  • 这些绑定在逻辑上被分组到一个名称下;你可以使用这个名称来卸载它们,使用 IKernel.Unload(string)

3
也许我在这里错过了一些显而易见的东西,但与创建普通的类并调用其方法或使用静态类和静态方法相比,这有什么好处呢?
对我们来说,这是非常容易添加测试的一种方式。只需使用模拟对象覆盖几个绑定,就可以在没有 DI 的遗留代码上添加测试用例,而在不进行一些重构的情况下几乎是不可能的。如果 DI 被正确地使用且将 DI 用于连接所有东西,则即使在可能非常丑陋的遗留代码上,也很容易这样做。
在许多 DI 框架中,您可以使用生产模块作为测试模块,并使用模拟对象覆盖特定的绑定(保留其余的连接)。这些可能是系统测试而不是单元测试,但与平均开发人员相比,我更喜欢更高级别的测试,因为它测试类之间的集成,并且对于加入项目并可以看到整个功能正在运行(而不仅仅是功能的某些部分)的人来说,这是很好的文档,而无需设置整个系统。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接