使用依赖注入,我发现自己创建了工厂来进行单元测试。这正常吗?

3
请查看以下简化的示例代码:
public class RequestManager
{
    public RequestManager()
    {

    }

    public void ProcessRequest(byte[] data)
    {
        IRequest request = new Request(data);
        request.Send();
    }
}

当我需要测试这段代码时,我通常会得到以下结果:
public class RequestManager
{
    private IRequestFactory requestFactory;

    public RequestManager(IRequestFactory requestFactory)
    {
        this.requestFactory = requestFactory;
    }

    public void ProcessRequest(byte[] data)
    {
        IRequest request = this.requestFactory.CreateRequest(data);
        request.Send();
    }
}

CreateRequest方法只是有一个新的Request(data),因此我可以模拟工厂并返回一个模拟对象。
问题是,我开始为一些简单的任务创建了许多工厂,我想知道这是否正常,或者是否有一种模式或另一种方法可以帮助我克服这个问题。
有什么想法吗?提前感谢。
2个回答

2
不仅这是正常的,而且也是良好设计的标志。在正确应用DI的应用程序中,您不需要为以下内容使用工厂:
从依赖注入容器解析的对象 POCO / DTO(因为它们是简单的值存储) 在大多数情况下,.NET BCL对象(尽管可能会有所不同)
对于其他所有内容,工厂都是一个好选择。为什么?
它提供了统一的对象创建方式(这样整个团队都知道当您需要IRequest实例时,应该注入IRequestFactory) 它不会引发任何不必要的问题(如“我应该在创建后设置SomeProperty?”或“我应该使用哪个构造函数?”。Factory.CreateInstance()是微不足道和直观的 - 不需要解释任何内容,所有决策已经在工厂内部制定和嵌入) 它与模拟结合使用可以极大地简化单元测试(特别是当无法轻松创建带有虚假数据的实际对象时)
除了单元测试之外,前两点在3、6、12个月后返回代码时具有极大的好处 - 工厂使开发人员从不应该需要做出任何决策的事情中解放出来。
您有一个非常简单的合同,可以回答所有可能的问题,而不是想知道使用哪个构造函数和传递哪些参数,您作为开发人员可以专注于实际问题。

1
IRequest request = new Request(data);

这行代码与依赖注入截然相反,它是一个硬编码的依赖关系。该行将 RequestManager 类与 IRequest 接口的 Request 实现绑定在一起。请注意保留 HTML 标签。

或许可以尝试这样做:

public void ProcessRequest(IRequest request)
{
   request.Send();
}

这样,RequestManager 就不需要了解任何 IRequest 实现。您可以将一个模拟对象/存根交给它,它甚至感觉不到!当然,这意味着创建其 RequestManager 的类需要知道该依赖项。但是,您可以以类似的方式交给它它的 RequestManager
最终,所有依赖关系都集中在一个单一点上。通常,我们使用一些框架来处理将它们连接在一起的事情,例如基于 XML 文件。
依赖注入的整个重点是通过 setter 或构造函数参数提供所需的具体对象。依赖注入框架允许您将依赖关系集中在单个位置,从那里您可以轻松地更改提供给其他需要它们的类的具体类。这样,您也可以在测试中使用模拟对象 - 非常方便! 在这里您可以找到有关依赖注入的精彩文章

调用 ProcessRequest 的方法并不了解请求内容,因为这个类是 RequestManager 内部的。我知道这违反了 DI 原则,所以最终我使用了一个工厂,因此产生了这个问题。 - Ignacio Soler Garcia
@SoMoS 请查看这篇文章,看看它是否能澄清任何问题。如果不能,请回来,我们会尽力解决 :) - K.L.
我已经多次阅读了这篇文章。它是一篇经典的文章,我正在使用构造函数注入。我只是想问这是否正常。 - Ignacio Soler Garcia
有兴趣加入聊天吗? - K.L.
@SoMoS,我不明白为什么你在RequestManagers构造函数中放置了一个工厂,而不是它实际需要的IRequest。如果我理解正确的话,这可以由您的DI框架处理。 - K.L.
ProcessRequest的调用者确实不应该知道任何关于请求的信息。你应该一直跟踪到组合根。 - Facio Ratio

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接