我理解依赖注入,但没有"啊"的时刻,当它点击时,我真正看到了光明。
我为什么要使用DI?另外,当模拟像那些使用文件系统的对象时,模拟对象能够做什么?它是否只是执行虚拟调用(因此并不真正使用文件系统)?
DI的目的是使代码松散耦合。根据定义,单元测试需要松耦合,因为如果许多类是紧密耦合的,那么它就不再是单元测试(而是集成测试)。
然而,DI的目的并不是启用单元测试,而是使您的代码库更易于维护。许多积极的副作用之一是,它也变得更加可测试。
在模拟文件系统时,过于紧密地镜像文件系统的各个方面基本上是一个坏主意,因为这将导致漏抽象。相反,您应该考虑使用流或类似的概念。
依赖注入就是不把依赖硬编码到组件中的做法。例如
class Service {
Collaborator c = new Collaborator()
}
伪代码是合作者硬编码的。这很难改变。如果你做了
class Service {
Collaborator c;
Service(Collaborator c) {
this.c = c;
}
}
现在你可以通过构造函数把想要的合作者"注入"到Service组件中。没有硬编码的依赖项。
这很好,因此您可以轻松地交换合作者的实现。你的代码现在是"松耦合的"——没有对特定实现的硬依赖,只有对类型和行为的依赖。
这样做的一个应用是,您现在可以通过在测试中注入模拟合作者来测试Service
,这样您就可以以一种不依赖于合作者的方式测试所有的服务功能。
在实践中,您希望Collaborator
是一个接口(或者您所选择的语言支持的任何等价的接口),以便您可以定义行为,并将实现留给您注入的实际实例。
你问题的第二部分,关于嘲笑一个做文件操作的合作者,是正确的。如果模拟文件系统协作器,则可以在不实际触及文件系统
让我从hvgotcodes的答案再往前走几步:
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
(这会破坏真正的"单元"测试)。
为了进一步讨论…
大多数时候,当人们谈论DI时,主要的论点将沿着可测试性的思路,但是,正如Mark Seeman指出的那样(顺便说一下,购买他关于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的内容,但松散耦合和可测试性总是首先指出的两个好处。
问候。