模拟问题和依赖注入

时间:2011-10-06 15:23:22

标签: dependency-injection

我理解依赖注入,但没有那个“啊”时刻,它点击的地方,我真的看到了光。

我为什么要使用DI?此外,当模拟像使用文件系统的对象时,模拟对象能够做什么?它是否只是进行虚拟调用(所以不真正使用文件系统)?

4 个答案:

答案 0 :(得分:2)

DI的目的是使代码松散耦合。根据定义,单元测试需要松散耦合,因为如果许多类紧密耦合,则不再是单元测试(而是集成测试)。

但是,DI的目的不是为了启用单元测试,而是为了使代码库更易于维护。许多积极的副作用之一是它也显得更加可测试。

当涉及到模拟文件系统时,过于密切地镜像文件系统的各个方面基本上是一个坏主意,因为这将导致 Leaky Abstraction 。相反,您应该考虑使用流或类似的概念。

答案 1 :(得分:1)

依赖注入只是不依赖于编码组件的实践。例如

class Service {
   Collaborator c = new Collaborator()
}

伪代码对协作者进行了硬编码。这很难改变。如果你做了

class Service {
    Collaborator c;

    Service(Collaborator c) {
       this.c = c;
    }
}

现在您可以通过构造函数将所需的协作者“注入”Service组件。没有硬编码的依赖。

这很好,因此您可以轻松地更换协作者的实现。您的代码现在“松散耦合” - 对特定实现没有硬性依赖,只有类型和行为。

这样做的一个应用是,您现在可以通过在测试中注入模拟协作者来测试Service,这样您就可以以不依赖于协作者的方式测试所有服务功能。

实际上,您希望Collaborator成为一个接口(或者您选择的语言支持的任何等价物),以便您可以定义行为,并将实现保留到您注入的实际实例。

关于模拟执行文件操作的协作者的问题的第二部分是正确的。如果您模拟文件系统协作者,则可以在不实际访问文件系统的情况下测试单独使用协作者的内容

答案 2 :(得分:1)

让我再远离hvgotcodes answer

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以进行骑行(这将打破真正的“单元”测试)。

答案 3 :(得分:0)

在讨论中添加更多内容......

大多数时候,当人们谈论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还有很多,但松耦合和可测试性总是首先指出的两个好处。

问候。