无法通过工厂 DI 理解 ninject(或一般的 IOC 容器)?
好吧,最近我一直在阅读 ninject,但我很难理解为什么他们在 wiki 页面上将 do 称为“穷人的”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)它假设存储库永远不会有任何构造函数(不正确),并且服务的唯一构造函数将是其相应的存储库(也不正确)。有人告诉我“嘿,这是你应该使用 IoC 容器的地方,在这里会很棒!”因此我的研究开始了......但我只是没有看到它并且无法理解它...
是否有某种方法 ninject 可以在没有特定声明的情况下自动解析类的构造函数,这样在我的通用工厂中使用它会很棒(我也意识到我可以使用反射手动执行此操作,但这会影响性能,并且 ninject在他们的页面上说他们不使用 反射)。
对于这个问题的启发和/或展示如何在我的通用工厂中使用它将非常感激!
编辑:答案
所以感谢下面的解释,我能够完全理解 ninject 的厉害之处,我的通用工厂看起来像这样:
public static class EntityServiceFactory
{
public static TServiceClass GetService<TServiceClass>()
where TServiceClass : class
{
IKernel kernel = new StandardKernel();
return kernel.Get<TServiceClass>();
}
}
非常棒。由于具体类具有隐式绑定,因此一切都会自动处理。
Okay, so recently I've been reading into ninject but I am having trouble understanding what makes it better over why they referred do as 'poor man's' DI on the wiki page. The sad thing is I went over all their pages on the wiki and still don't get it =(.
Typically I will wrap my service classes in a factory pattern that handles the DI like so:
public static class SomeTypeServiceFactory
{
public static SomeTypeService GetService()
{
SomeTypeRepository someTypeRepository = new SomeTypeRepository();
return = new SomeTypeService(someTypeRepository);
}
}
Which to me seems a lot like the modules:
public class WarriorModule : NinjectModule {
public override void Load() {
Bind<IWeapon>().To<Sword>();
Bind<Samurai>().ToSelf().InSingletonScope();
}
}
Where each class would have it's associated module and you Bind it's constructor to a concrete implementation. While the ninject code is 1 less line I am just not seeing the advantage, anytime you add/remove constructors or change the implementation of an interface constructor, you'd have to change the module pretty much the same way as you would in the factory no? So not seeing the advantage here.
Then I thought I could come up with a generic convention based factory like so:
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;
}
However, this is crappy for 2 reasons: 1) Its tightly dependent on the naming convention, 2) It assumed the repository will never have any constructors (not true) and the service's only constructor will be it's corresponding repo (also not true). I was told "hey this is where you should use an IoC container, it would be great here!" And thus my research began...but I am just not seeing it and am having trouble understanding it...
Is there some way ninject can automatically resolve constructors of a class without a specific declaration such that it would be great to use in my generic factory (I also realize I could just do this manually using reflection but that's a performance hit and ninject says right on their page they don't use reflection).
Enlightment on this issue and/or showing how it could be used in my generic factory would be much appreciated!
EDIT: Answer
So thanks to the explanation below I was ably to fully understand the awesomeness of ninject and my generic factory looks like this:
public static class EntityServiceFactory
{
public static TServiceClass GetService<TServiceClass>()
where TServiceClass : class
{
IKernel kernel = new StandardKernel();
return kernel.Get<TServiceClass>();
}
}
Pretty awesome. Everything is handled automatically since concrete classes have implicit binding.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
IoC 容器的好处随着项目的规模而增长。对于小型项目,与工厂等“穷人的直接投资”相比,它们的收益微乎其微。想象一个拥有数千个类的大型项目,并且一些服务在许多类中使用。在这种情况下,您只需说出一次这些服务是如何解决的。在工厂里,每一堂课你都必须一遍又一遍地做。
示例:如果您有一个服务
MyService : IMyService
和一个需要IMyService
的类A
,您必须告诉 Ninject 如何解析这些类型,例如在你的工厂里。在这里,好处是微乎其微的。但是,一旦您的项目增长并添加了一个也依赖于IMyService
的类B
,您只需告诉 Ninject 如何解析B
即可。 Ninject 已经知道如何获取IMyService
。另一方面,在工厂中,您必须再次定义 B 如何获取其IMyService
。更进一步。在大多数情况下,您不应该一一定义绑定。而是使用基于约定的配置(
Ninject.Extension.Conventions
)。这样,您可以将类(服务、存储库、控制器、演示器、视图……)分组在一起,并以相同的方式配置它们。例如告诉Ninject,所有以Service 结尾的类都应该是单例并发布它们的所有接口。这样您就拥有一个配置,并且在添加另一项服务时无需进行任何更改。此外,IoC 容器不仅仅是工厂。还有更多。例如生命周期管理、拦截……
The benefit of IoC containers grows with the size of the project. For small projects their benefit compared to "Poor Man's DI" like your factory is minimal. Imagine a large project which has thousands of classes and some services are used in many classes. In this case you only have to say once how these services are resolved. In a factory you have to do it again and again for every class.
Example: If you have a service
MyService : IMyService
and a classA
that requiresIMyService
you have to tell Ninject how it shall resolve these types like in your factory. Here the benefit is minimal. But as soon as you project grows and you add a classB
which also depends onIMyService
you just have to tell Ninject how to resolveB
. Ninject knows already how to get theIMyService
. In the factory on the other hand you have to define again how B gets itsIMyService
.To take it one step further. You shouldn't define bindings one by one in most cases. Instead use convention based configuration (
Ninject.Extension.Conventions
). With this you can group classes together (Services, Repositories, Controllers, Presenters, Views, ....) and configure them in the same way. E.g. tell Ninject that all classes which end with Service shall be singletons and publish all their interfaces. That way you have one single configuration and no change is required when you add another service.Also IoC containers aren't just factories. There is much more. E.g. Lifecycle managment, Interception, ....
要完全类似,您的工厂代码应为:
Ninject 中的等效项为:
在这里,您可以以声明方式定义依赖项,确保测试代码和生产代码之间的唯一差异包含在设置阶段。
IoC 的优点并不是每次接口或构造函数更改时都不必更改模块,而是您可以以声明方式声明依赖项,并且可以为不同的目的即插即用不同的模块。
To be fully analagous your factory code should read:
And the equilvalent in Ninject would be:
Here, you can define the dependencies declaratively, ensuring that the only differences between your testing and production code are contained to the setup phase.
The advantage of an IoC is not that you don't have to change the module each time the interface or constructor changes, it's the fact that you can declare the dependencies declaratively and that you can plug and play different modules for different purposes.