在工厂DI上无法理解ninject(或者只是IOC容器)?

时间:2012-01-26 16:38:25

标签: c# inversion-of-control ninject ioc-container

好吧,最近我一直在阅读ninject,但我很难理解是什么让它更好地解释为什么他们在维基页面上提到'穷人'的DI。可悲的是我在wiki上浏览了所有页面但仍然没有得到它=(。

通常我会将我的服务类包装在处理DI的工厂模式中,如下所示:

public static class SomeTypeServiceFactory
{
    public static SomeTypeService GetService()
    {
        SomeTypeRepository someTypeRepository = new SomeTypeRepository();
        return = new SomeTypeService(someTypeRepository);

    }

}

对我而言似乎很像模块:

public class WarriorModule : NinjectModule {
    public override void Load() {
      Bind<IWeapon>().To<Sword>();
      Bind<Samurai>().ToSelf().InSingletonScope();
    }
}

每个类都有它的关联模块,你将它的构造函数绑定到一个具体的实现。虽然ninject代码少了1行,但我没有看到优势,无论何时添加/删除构造函数或更改接口构造函数的实现,您都必须更改模块,就像在工厂中一样没有?所以没有看到这里的优势。

然后我想我可以想出一个基于通用惯例的工厂:

 public static TServiceClass GetService<TServiceClass>()
        where TServiceClass : class
    {
        TServiceClass serviceClass = null;

        string repositoryName = typeof(TServiceClass).ToString().Replace("Service", "Repository");
        Type repositoryType = Type.GetType(repositoryName);

        if (repositoryType != null)
        {
            object repository = Activator.CreateInstance(repositoryType);
            serviceClass =  (TServiceClass)Activator.CreateInstance(typeof (TServiceClass), new[]{repository});
        }

        return serviceClass;
    }

然而,由于两个原因,这很糟糕:1)它严格依赖于命名约定,2)它假设存储库永远不会有任何构造函数(不是真的),而服务的唯一构造函数将是它的相应repo(也不是真正)。我被告知“嘿,这是你应该使用IoC容器的地方,这里会很棒!”因此我的研究开始......但我只是没有看到它并且无法理解它...

有没有某种方法ninject可以自动解析类的构造函数而没有特定的声明,这样在我的通用工厂中使用它会很棒(我也意识到我可以使用反射手动执行此操作但这是性能损失和ninject在他们的页面上说他们不使用反射。)

对此问题的启示和/或展示如何在我的仿制药厂中使用它将非常感激!

编辑:回答

因此,由于下面的解释,我非常了解ninject的真棒,我的通用工厂看起来像这样:

public static class EntityServiceFactory
{
    public static TServiceClass GetService<TServiceClass>()
        where TServiceClass : class
    {
        IKernel kernel = new StandardKernel();

        return kernel.Get<TServiceClass>();
    }
}

非常棒。由于具体类具有隐式绑定,因此所有内容都会自动处理。

2 个答案:

答案 0 :(得分:7)

IoC容器的好处随着项目的规模而增长。对于小型项目而言,他们的好处与“穷人的DI”相比,就像您的工厂一样。想象一个大型项目,它有数千个类,一些服务在很多类中使用。在这种情况下,您只需要说一次如何解决这些服务。在工厂里,你必须为每个班级一次又一次地做这件事。

示例:如果您有服务MyService : IMyService和类A需要IMyService,您必须告诉Ninject如何解决这些类型,例如您的工厂。这里的好处很少。但是一旦项目增长并添加了一个类B,这也取决于IMyService,您只需要告诉Ninject如何解决B。 Ninject已经知道如何获得IMyService。另一方面,在工厂中,您必须再次定义B如何获得其IMyService

更进一步。在大多数情况下,您不应该逐个定义绑定。而是使用基于约定的配置(Ninject.Extension.Conventions)。通过这种方式,您可以将类组合在一起(服务,存储库,控制器,演示者,视图......)并以相同的方式对它们进行配置。例如。告诉Ninject所有以Service结尾的类都应该是单例并发布所有接口。这样,您只需一个配置,并且在添加其他服务时无需进行任何更改。

IoC容器也不仅仅是工厂。还有更多。例如。生命周期管理,拦截,......

kernel.Bind(
    x => x.FromThisAssembly()
          .SelectAllClasses()
          .InNamespace("Services")
          .BindToAllInterfaces()
          .Configure(b => b.InSingletonScope()));
kernel.Bind(
    x => x.FromThisAssembly()
          .SelectAllClasses()
          .InNamespace("Repositories")
          .BindToAllInterfaces());

答案 1 :(得分:2)

要完全类似,您的工厂代码应为:

public static class SomeTypeServiceFactory
{
    public static ISomeTypeService GetService()
    {
        SomeTypeRepository someTypeRepository = new SomeTypeRepository();

        // Somewhere in here I need to figure out if i'm in testing mode 
        // and i have to do this in a scope which is not in the setup of my
            // unit tests

        return new SomeTypeService(someTypeRepository);
    }

    private static ISomeTypeService GetServiceForTesting()
    {
        SomeTypeRepository someTypeRepository = new SomeTypeRepository();
        return new SomeTestingTypeService(someTypeRepository);
    }
}

Ninject中的等价是:

public class WarriorModule : NinjectModule {
    public override void Load() {
      Bind<ISomeTypeService>().To<SomeTypeService>();
    }
}

public class TestingWarriorModule : NinjectModule {
    public override void Load() {
      Bind<ISomeTypeService>().To<SomeTestingTypeService>();
    }
}

在这里,您可以声明性地定义依赖关系,确保测试和生产代码之间的唯一差异包含在设置阶段。

IoC的优点并不是每次接口或构造函数更改时都不必更改模块,事实上您可以声明性地声明依赖项,并且可以为不同目的插入和播放不同的模块