与 SOLID 和依赖注入混淆

Confused with SOLID and Dependecy Injection

所以我正在尝试学习 SOLID 原则和依赖注入。我已经阅读了一些有关该主题的博客文章,并且开始有所了解。但是,有一种情况我找不到答案,将尝试在这里解释。

我开发了一个库来进行文本匹配,它包含一个 class Matcher,它有一个名为 Match 的函数,returns 结果在 MatchResult 对象。该对象包含诸如百分比、经过的时间、是否成功等信息。现在,根据我对依赖注入的理解,高级 class 不应该 "know" 关于低级 class 或模块。所以我已经设置了我的库和 Matcher class 以使用 Matcher class 的接口,这将允许我使用 IoC 容器注册它。但是,因为该函数返回一个 MatchResult 对象,所以 "high level class" 必须知道 MatchResult 对象,这违反了 DI 的规则。

我应该如何解决这个问题,推荐的方法是什么?

DI 声明使用接口的 classes 不应该知道具体实现的任何细节。但是,MatchResult 不是实现细节,而是接口契约的一部分(DTO,Match 方法的 return 类型)——没关系。您可以有一个额外的 class 以不同的方式实现该 IMatcher 接口,但它仍然应该 return 一个 MatchResult,就像它所期望的那样。

你应该区分DIP、IoC和DI:

DIP - 依赖倒置原则 - 它只说我们应该做什么,不应该做什么

IoC - 控制反转 - 一种使用 DIP 的方式,它基本上是一种应用模式 DIP.IoC 描述了反转控制的不同方式

DI - 它是 IoC 的具体实现(如服务定位器、上下文查找、工厂)

DIP, IoC, DI

the "high level class" will have to know about the MatchResult object which breaks the rules of DI.

不,这不会违反 DIP 规则。 您不应该了解具体的实现 - 在这种情况下,您应该使用接口(即 IMatcher)并创建实现此接口的 classes。

然后你可以使用任何实现这个接口的class:

public class Test
{
    private IMatcher _matcher;

    public Test(IMatcher matcher) // here you can pass any class than implements IMatcher, and you don't know specific implementation of this class - this is DI
    {
        this._matcher = matcher;
    }
}

在您的情况下,MatchResult 只是 return 由某些实现编辑的结果。

如果您想 return 不同的 MatchResult 实现,您也可以使用 IMatchResult 接口而不是您想要的 return 实现的特定实现。

"High level" 和 "low level" 是与 dependency inversion 相关联的术语,它与依赖注入有关,但是是不同的概念。它们都有首字母 "DI",并且两者中的 "D" 都代表 "dependency,",因此它们会造成一些混淆。

(我是这样想的——依赖注入是实现依赖倒置的一种方式。)

但在我看来,定义依赖倒置时使用的术语可能会让试图理解该概念的 .NET 程序员感到困惑。它适用,但 .NET 开发人员不使用某些术语。

根据维基百科引用的罗伯特·马丁的定义,

High-level modules should not depend on low-level modules. Both should depend on abstractions. Abstractions should not depend on details. Details should depend on abstractions.

什么是 "high level module" 和 "low level module?" 如果您发现这种困惑,您并不孤单。我们并没有真正使用这些术语。我们真正能够理解和应用的部分是我们应该依赖抽象.

MatchResult 的情况下,如果它只是一些属性的容器,那么它可能已经足够抽象了。 DTO 已经成为一种常见的做法有一段时间了,所以如果时间表明我们需要将它们包装在接口中,那么这种模式现在应该已经出现了。不痛,但通常没有必要。

回到依赖倒置——真正的困惑来自名称本身。倒转是怎么回事?当您看到类似于维基百科页面上的图表时,我的建议是将目光从令人眼花缭乱的图表上移开。

Martin explains 他是这样使用这个词的 "inversion" (回到他关于这个主题的原始论文)

One might question why I use the word “inversion”. Frankly, it is because more traditional software development methods, such as Structured Analysis and Design, tend to create software structures in which high level modules depend upon low level modules, and in which abstractions depend upon details. Indeed one of the goals of these methods is to define the subprogram hierarchy that describes how the high level modules make calls to the low level modules. Figure 1 is a good example of such a hierarchy. Thus, the dependency structure of a well designed object oriented program is “inverted” with respect to the dependency structure that normally results from traditional procedural methods.

换句话说,倒置是应用依赖倒置和应用的"traditional"风格之间的对比依赖倒置。如果你来自 'high level modules depend upon low level modules,' 的背景(并且你使用术语 "module."),这可能会更清楚但是如果那不是你以前的 'tradition' 那么你是什么 'inverting?' 没有。

所有这些细节仍然有意义,但当您第一次尝试学习这些概念时,它们会非常混乱。我的建议是应用这部分,因为你已经是:取决于抽象。

如果你这样做,那么你就是在应用这个原则,因为无论 "high-level modules" 和 "low-level modules" 是什么,你的 类 都不会太依赖其他 类 - 高级、低级或其他。