目前我正在尝试更好地理解依赖注入,并且我正在使用asp.net MVC来处理它。你可能会看到我的一些其他相关问题;)
好的,我将从一个示例控制器(示例Contacts Manager 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>();
但我的问题是,**我解决了什么问题或者通过在这种特定情况下使用依赖注入简化了什么“
我现在没有看到任何实际用途,我理解如何但不是为什么?这有什么用?任何人都可以添加到这个项目中,举一个例子说明这是如何更实用和有用的吗?
答案 0 :(得分:10)
第一个示例不是单元可测试的,因此它不好,因为它在应用程序的不同层之间创建了强耦合,并使它们不再可重用。第二个示例称为poor man dependency injection。它还讨论了here。
穷人依赖注入的错误在于代码不是自动记录的。它没有向消费者说明其意图。消费者看到这个代码,他可以很容易地调用默认构造函数而不传递任何参数,而如果没有默认构造函数,它会立即清楚这个类绝对需要将一些契约传递给它的构造函数才能正常运行。并且真的不是要由班级来决定选择哪种具体实施方式。这取决于这个类的消费者。
答案 1 :(得分:5)
依赖注入有三个主要原因:
作为示例 - 考虑单元测试,该测试需要访问定义为接口的类。在许多情况下,接口的单元测试必须调用该接口的实现 - 因此如果实现发生更改,单元测试也会发生变化。但是,使用DI,您可以在运行时将接口的实现“注入”到使用注入API的单元测试中 - 因此实现的更改只需要由注入框架处理,而不是由使用这些实现的单个类处理。
另一个例子是在网络世界中:考虑服务提供者和服务定义之间的耦合。如果特定组件需要访问服务 - 最好设计界面而不是该服务的特定实现。通过允许您通过引用注入框架动态添加依赖关系,Injection还支持此类设计。
因此,类别之间的各种联系被移出工厂和个别班级,并且当具有良好的DI框架时,以统一,抽象,可重复使用且易于维护的方式处理。我见过的有关DI的最佳教程是关于Google的Guice教程,可在YouTube上找到。虽然这些与您的特定技术不同,但原则是相同的。
答案 2 :(得分:0)
首先,您的示例将无法编译。 var _db;
不是有效语句,因为必须在声明时推断变量的类型。
您可以执行var _db = new ContactsManagerDb();
,但是您的第二个构造函数将无法编译,因为您尝试将IContactsManagerDb分配给ContactsManagerDb的实例。
您可以将其更改为IContactsManagerDb _db;
,然后确保ContactsManagerDb
派生自IContactsManagerDb
,但这会使您的第一个构造函数更加重要。你必须拥有接受参数的构造函数,所以为什么不一直使用它呢?
依赖注入就是要从类本身中删除依赖项。 ContactsController
无需了解ContactsManagerDb即可使用IContactsManagerDb访问联系人管理器。