I've found that correctly implementing Dependency Injection tends to force programmers to use a variety of other programming practices that help to improve the testability, flexibility, maintainability, and scalability of code: practices like the Single Responsibility Principle, Separations of Concerns, and coding against APIs. It feels like I'm being compelled to write more modular, bite-sized classes and methods, which makes the code easier to read, because it can be taken in bite-sized chunks.
但它也倾向于创建相当大的依赖树,通过框架(特别是如果您使用约定)比手工管理要容易得多。今天我想在LINQPad中快速测试一些东西,我认为创建内核并在我的模块中加载太麻烦了,最后我手写了这个:
var merger = new SimpleWorkflowInstanceMerger(
new BitFactoryLog(typeof(SimpleWorkflowInstanceMerger).FullName),
new WorkflowAnswerRowUtil(
new WorkflowFieldAnswerEntMapper(),
new ActivityFormFieldDisplayInfoEntMapper(),
new FieldEntMapper()),
new AnswerRowMergeInfoRepository());
回想起来,使用IoC框架会更快,因为模块按照约定定义了几乎所有这些东西。
Having spent some time studying the answers and comments on this question, I am convinced that the people who are opposed to using an IoC container aren't practicing true dependency injection. The examples I've seen are of practices that are commonly confused with dependency injection. Some people are complaining about difficulty "reading" the code. If done correctly, the vast majority of your code should be identical when using DI by hand as when using an IoC container. The difference should reside entirely in a few "launching points" within the application.
换句话说,如果你不喜欢IoC容器,你可能没有按照它应该做的方式来做依赖注入。
另一点:如果你在任何地方都使用反射,依赖注入真的不能手工完成。虽然我讨厌反射对代码导航造成的影响,但您必须认识到,在某些领域,它确实是无法避免的。ASP。例如,NET MVC尝试通过对每个请求的反射来实例化控制器。要手动进行依赖注入,你必须让每个控制器都成为“上下文根”,如下所示:
public class MyController : Controller
{
private readonly ISimpleWorkflowInstanceMerger _simpleMerger;
public MyController()
{
_simpleMerger = new SimpleWorkflowInstanceMerger(
new BitFactoryLog(typeof(SimpleWorkflowInstanceMerger).FullName),
new WorkflowAnswerRowUtil(
new WorkflowFieldAnswerEntMapper(),
new ActivityFormFieldDisplayInfoEntMapper(),
new FieldEntMapper()),
new AnswerRowMergeInfoRepository())
}
...
}
现在将其与允许DI框架为你做这件事进行比较:
public MyController : Controller
{
private readonly ISimpleWorkflowInstanceMerger _simpleMerger;
public MyController(ISimpleWorkflowInstanceMerger simpleMerger)
{
_simpleMerger = simpleMerger;
}
...
}
使用DI框架,注意:
I can unit-test this class. By creating a mock ISimpleWorkflowInstanceMerger, I can test that it gets used the way I anticipate, without the need for a database connection or anything.
I use far less code, and the code is much easier to read.
If one of my dependency's dependency's changes, I don't have to make any changes to the controller. This is especially nice when you consider that multiple controllers are likely to use some of the same dependencies.
I never explicitly reference classes from my data layer. My web application can just include a reference to the project containing the ISimpleWorkflowInstanceMerger interface. This allows me to break the application up into separate modules, and maintain a true multi-tier architecture, which in turn makes things much more flexible.
A typical web application will have quite a few controllers. All of the pain of doing DI by hand in each controller will really add up as your application grows. If you have an application with only one context root, which never tries to instantiate a service by reflection, then this isn't as big a problem. Nevertheless, any application that uses Dependency Injection will become extremely expensive to manage once it reaches a certain size, unless you use a framework of some kind to manage the dependency graph.