我理解依赖注入,但没有那个“啊”的时刻,点击它,我真的看到了光。嘲笑的问题和依赖注入
我为什么要用DI?另外,当嘲讽那些使用文件系统的对象时,模拟对象能够做什么?它只是做虚拟调用(所以不真的使用文件系统)?
我理解依赖注入,但没有那个“啊”的时刻,点击它,我真的看到了光。嘲笑的问题和依赖注入
我为什么要用DI?另外,当嘲讽那些使用文件系统的对象时,模拟对象能够做什么?它只是做虚拟调用(所以不真的使用文件系统)?
让我去一对夫妇从hvgotcodesanswer几步远:
class Service {
Collaborator c = new Collaborator()
}
是你原来的班用硬编码的依赖性。
class Service {
Collaborator c;
Service(Collaborator c) {
this.c = c;
}
}
是你的注入依赖的新类。
到目前为止,这么好。现在,让我们取Collaborator
并从中提取一个接口;称它为ICollaborator
。现在你的新玩家看起来像这样:
class Service {
ICollaborator c;
Service(ICollaborator c) {
this.c = c;
}
}
这是什么给你买的?那么,你可以在你的代码,创建该类的行为像第一个例子是这样的:
// w00t! My code compiles and works again! Ship it!
Service myService = new Service(new Collaborator());
漂亮添油加醋容易。当你想要使用不同类型的Collaborator
时,美丽来自于 - 也许甚至是模拟或假的。只要它实现了接口ICollaborator
,你就是金:
// I'm using Fake It Easy for this example.
Service myService = new Service(A.Fake<ICollaborator>());
瞧!你现在有一个单元可测试的Service
实例,它不会拖拽混凝土Collaborator
(这将打破真正的“单元”测试)。
依赖注入只是一种不硬编码依赖到组件的做法。例如,
class Service {
Collaborator c = new Collaborator()
}
该伪码具有合作者硬编码。这很难改变。如果你没有
class Service {
Collaborator c;
Service(Collaborator c) {
this.c = c;
}
}
现在你可以通过构造函数'注入'所需的协作者到服务组件中。没有硬编码的依赖关系。
这很好,您可以轻松地更换协作者的实现。您的代码现在“松散耦合” - 对特定实现没有硬性依赖关系,仅限于类型和行为。
这样做的一个应用是,您现在可以在测试中注入一个模拟协作器来测试Service
,这样您就可以以不依赖协作者的方式测试所有服务功能。
实际上,您希望Collaborator
成为一个接口(或任何与您的选择语言支持相当的接口),以便您可以定义行为并将实现留给您注入的实际实例。
问题的第二部分,关于嘲笑执行文件操作的协作者是正确的。如果您嘲笑文件系统协作者,则可以单独测试使用协作者而不实际击中文件系统的内容
DI的目的是使代码松散耦合。根据定义,松散耦合需要单元测试,因为如果许多类紧密耦合,它不再是单元测试(而是集成测试)。
但是,DI的目的不是为了启用单元测试,而是为了让您的代码库更易于维护。许多积极的副作用之一是它也变得非常可测试。
当谈到嘲讽文件系统,它基本上是一个坏主意,文件系统等方面过于紧密镜子,因为这将导致Leaky Abstraction。相反,你应该考虑使用流或类似的概念。
要添加更多的讨论......
很多时候人们谈论DI的主要论点将沿着可测性的线条,但正如马克·西曼指出的(顺便说一下买他的书的时候关于DI,非常有启发性,对广告抱歉)最重要的方面是让你的应用程序松耦合,因此更易于维护。
要提供一个例子,在其他的答案中相同的代码:
比方说,你得到的是,根据新的要求....我不知道....一年的时间,你需要使用不同的合作者,你可以这样做以下:
ICollaborator collaborator;
switch(timeOfYear)
{
case "Spring":
collaborator = new SpringCollaborator();
break;
case "Summer":
collaborator = new SummerCollaborator();
break;
case "Fall":
collaborator = new FallCollaborator();
break;
case "Winter":
collaborator = new WinterCollaborator();
break;
}
Service myService = new Service(collaborator);
这种方式,因为你需要和你的服务将永远不需要改变,因为它不关心你可以创建许多实现只要它实现ICollaborator接口,协作者的详细信息。
有很多关于DI的更多信息,但松耦合和可测性始终是首先指出的两个好处。
问候。
我明白了。我明白,这是一个类似日期时间的好处。如果datetime从一个接口派生,并且我从同一个接口创建另一个类,那么这些方法可以是空的存根吗? – dotnetdev
准确!现在,任何具有返回类型的方法都应该为该方法返回合理的值,但void方法可以是完全空的存根。 –