依赖注入有什么大的改进?
目前我正在尝试更好地理解依赖注入,并且我正在使用 asp.net MVC 来处理它。您可能会从我这里看到一些其他相关问题;)
好吧,我将从一个示例控制器(示例联系人管理器 asp.net MVC 应用程序)开始。
public class ContactsController{
ContactsManagerDb _db;
public ContactsController(){
_db = ContactsManagerDb();
}
//...Actions here
}
好吧,太棒了,它正在工作。我的操作都可以使用数据库进行CRUD操作。现在我决定要添加单元测试,并且我添加了另一个构造函数来模拟数据库
public class ContactsController{
IContactsManagerDb _db;
public ContactsController(){
_db = ContactsManagerDb();
}
public ContactsController(IContactsManagerDb db){
_db = db;
}
//...Actions here
}
太棒了,这正在起作用,在我的单元测试中我可以创建自己的 IContactsManagerDb
实现,并且对我的控制器进行单元测试。
现在,人们通常做出以下决定(这是我的实际问题),摆脱空控制器,并使用依赖项注入来定义要使用的实现。
因此,使用 StructureMap,我添加了以下注入规则:
x.For<IContactsManagerDb>().Use<ContactsManagerDb>();
当然,在我的测试项目中,我使用不同的 IContactsManagerDb
实现。
x.For<IContactsManagerDb>().Use<MyTestingContactsManagerDb>();
但我的问题是,**在这种特定情况下我通过使用依赖注入解决了什么问题或者简化了什么”
我现在看不到它的任何实际用途,我理解如何但不知道为什么?有什么用也许有人可以添加到这个项目中,举个例子,这如何更实用和有用?
Currently I'm trying to understand dependency injection better and I'm using asp.net MVC to work with it. You might see some other related questions from me ;)
Alright, I'll start with an example controller (of an example Contacts Manager asp.net MVC application)
public class ContactsController{
ContactsManagerDb _db;
public ContactsController(){
_db = ContactsManagerDb();
}
//...Actions here
}
Allright, awesome that's working. My actions can all use the database for CRUD actions. Now I've decided I wanted to add unit testing, and I've added another contructor to mock a database
public class ContactsController{
IContactsManagerDb _db;
public ContactsController(){
_db = ContactsManagerDb();
}
public ContactsController(IContactsManagerDb db){
_db = db;
}
//...Actions here
}
Awesome, that's working to, in my unit tests I can create my own implementation of the IContactsManagerDb
and unit test my controller.
Now, people usually make the following decision (and here is my actual question), get rid of the empty controller, and use dependency injection to define what implementation to use.
So using StructureMap I've added the following injection rule:
x.For<IContactsManagerDb>().Use<ContactsManagerDb>();
And ofcourse in my Testing Project I'm using a different IContactsManagerDb
implementation.
x.For<IContactsManagerDb>().Use<MyTestingContactsManagerDb>();
But my question is, **What problem have I solved or what have I simplified by using dependency injection in this specific case"
I fail to see any practical use of it now, I understand the HOW but not the WHY? What's the use of this? Can anyone add to this project perhaps, make an example how this is more practical and useful?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
第一个示例不可进行单元测试,因此它不好,因为它在应用程序的不同层之间创建了强耦合,并使它们的可重用性降低。第二个示例称为 < em>穷人依赖注入。还讨论了 这里。
穷人依赖注入的问题是代码不是自动记录的。它没有向消费者说明其意图。消费者看到这段代码,他可以轻松地调用默认构造函数而不传递任何参数,而如果没有默认构造函数,那么就会立即清楚该类绝对需要将一些契约传递给其构造函数才能正常运行。实际上,并不是由类来决定选择哪个具体实现。这取决于此类消费者。
The first example is not unit testable, so it is not good as it is creating a strong coupling between the different layers of your application and makes them less reusable. The second example is called poor man dependency injection. It's also discussed here.
What is wrong with poor man dependency injection is that the code is not autodocumenting. It doesn't state its intent to the consumer. A consumer sees this code and he could easily call the default constructor without passing any argument, whereas if there was no default constructor it would have immediately been clear that this class absolutely requires some contract to be passed to its constructor in order to function normally. And it is really not to the class to decide which specific implementation to choose. It is up to the consumer of this class.
依赖注入之所以有用,主要有 3 个原因:
作为一个例子 - 考虑需要访问定义为接口的类的单元测试。在许多情况下,接口的单元测试必须调用该接口的实现——因此,如果实现发生变化,单元测试也会发生变化。然而,通过 DI,您可以使用注入 API 在运行时将接口的实现“注入”到单元测试中,这样对实现的更改只需由注入框架处理,而不是由使用这些实现的各个类来处理。
另一个例子是在网络世界中:考虑服务提供者和服务定义之间的耦合。如果特定组件需要访问服务 - 最好针对接口进行设计,而不是针对该服务的特定实现进行设计。注入再次允许您通过引用注入框架动态添加依赖项,从而实现这种设计。
因此,当拥有良好的 DI 框架时,类之间的各种耦合就会从工厂和单个类中移出,并以统一、抽象、可重用且易于维护的方式进行处理。我见过的最好的 DI 教程是 Google 的 Guice 教程,可以在 YouTube 上找到。尽管这些与您的特定技术不同,但原理是相同的。
Dependency injection is useful for 3 main reasons :
As an example - consider the Unit test which required access to a class, defined as an interface. In many cases, a unit test for an interface would have to invoke implementations of that interface -- thus if an implementation changed, so would the unit test. However, with DI, you could "inject" an interface's implementation at run time into a unit test using the injection API - so that changes to implementations only have to be handled by the injection framework, not by individual classes that use those implementations.
Another example is in the web world : Consider the coupling between service providers and service definitions. If a particular component needs access to a service - it is better to design to the interface than to a particular implementation of that service. Injection enables such design, again, by allowing you to dynamically add dependencies by referencing your injection framework.
Thus, the various couplings of classes to one another are moved out of factories and individual classes, and dealt with in a uniform, abstract, reusable, and easily-maintained manner when one has a good DI framework. The best tutorials on DI that I have seen are on Google's Guice tutorials, available on YouTube. Although these are not the same as your particular technology, the principles are identical.
首先,您的示例无法编译。
var _db;
不是有效的语句,因为必须在声明时推断变量的类型。您可以执行 var _db = new ContactsManagerDb(); ,但是您的第二个构造函数将无法编译,因为您试图将 IContactsManagerDb 分配给 ContactsManagerDb 的实例。
您可以将其更改为
IContactsManagerDb _db;
,然后确保ContactsManagerDb
派生自IContactsManagerDb
,但这会使您的第一个构造函数变得无关紧要。无论如何,您都必须拥有接受接口参数的构造函数,那么为什么不一直使用它呢?依赖注入就是从类本身中删除依赖关系。
ContactsController
不需要了解 ContactsManagerDb 即可使用 IContactsManagerDb 访问联系人管理器。First, your example won't compile.
var _db;
is not a valid statement because the type of the variable has to be inferred at declaration.You could do
var _db = new ContactsManagerDb();
, but then your second constructor won't compile because you're trying to assign an IContactsManagerDb to an instance of ContactsManagerDb.You could change it to
IContactsManagerDb _db;
, and then make sure thatContactsManagerDb
derives fromIContactsManagerDb
, but then that makes your first constructor irrelvant. You have to have the constructor that takes the interface argument anyways, so why not just use it all the time?Dependency Injection is all about removing dependancies from the classes themselves.
ContactsController
doesn't need to know about ContactsManagerDb in order to use IContactsManagerDb to access the Contacts Manager.