Dependency Inversion Principle - 依赖性倒置原则

原文链接:Dependency Inversion Principle

Motivation

动机

When we design software applications we can consider the low level classes the classes which implement basic and primary operations(disk access, network protocols,...) and high level classes the classes which encapsulate complex logic(business flows, ...). The last ones rely on the low level classes. A natural way of implementing such structures would be to write low level classes and once we have them to write the complex high level classes. Since high level classes are defined in terms of others this seems the logical way to do it. But this is not a flexible design. What happens if we need to replace a low level class?

当我们设计软件应用程序时,我们可以认为低级别类实现基本和主要操作(磁盘访问,网络协议......)和高级类,这些类封装了复杂的逻辑(业务流,......)。 最后一个依赖于低级别的课程。 实现这种结构的一种自然方式是编写低级类,一旦我们让它们编写复杂的高级类。 由于高级类是根据其他类别定义的,因此这似乎是合乎逻辑的方法。 但这不是一个灵活的设计。 如果我们需要更换低级别课程会怎样?

Let's take the classical example of a copy module which reads characters from the keyboard and writes them to the printer device. The high level class containing the logic is the Copy class. The low level classes are KeyboardReader and PrinterWriter.

我们来看一个复制模块的经典例子,它从键盘读取字符并将它们写入打印机设备。 包含逻辑的高级类是Copy类。 低级类是KeyboardReader和PrinterWriter。

In a bad design the high level class uses directly and depends heavily on the low level classes. In such a case if we want to change the design to direct the output to a new FileWriter class we have to make changes in the Copy class. (Let's assume that it is a very complex class, with a lot of logic and really hard to test).

在糟糕的设计中,高级类直接使用并且在很大程度上依赖于低级类。 在这种情况下,如果我们想要更改设计以将输出定向到新的FileWriter类,我们必须在Copy类中进行更改。 (我们假设它是一个非常复杂的类,有很多逻辑并且很难测试)。

In order to avoid such problems we can introduce an abstraction layer between high level classes and low level classes. Since the high level modules contain the complex logic they should not depend on the low level modules so the new abstraction layer should not be created based on low level modules. Low level modules are to be created based on the abstraction layer.

为了避免这些问题,我们可以在高级类和低级类之间引入一个抽象层。 由于高级模块包含复杂逻辑,因此它们不应依赖于低级模块,因此不应基于低级模块创建新的抽象层。 基于抽象层创建低级模块。

According to this principle the way of designing a class structure is to start from high level modules to the low level modules:
High Level Classes --> Abstraction Layer --> Low Level Classes

根据这个原则,设计类结构的方法是从高级模块开始到低级模块:
高级类 - >抽象层 - >低级类

Intent

意图

  • 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.

  • 高级模块不应该依赖于低级模块。 两者都应该取决于抽象。

  • 抽象不应取决于细节。 细节应取决于抽象。

Example

例子

Below is an example which violates the Dependency Inversion Principle. We have the manager class which is a high level class, and the low level class called Worker. We need to add a new module to our application to model the changes in the company structure determined by the employment of new specialized workers. We created a new class SuperWorker for this.

以下是违反依赖性倒置原则的示例。 我们有一个高级类的经理类,以及名为Worker的低级类。 我们需要在我们的应用程序中添加一个新模块,以模拟公司结构的变化,这些变化取决于新专业人员的雇用。 我们为此创建了一个新的类SuperWorker。

Let's assume the Manager class is quite complex, containing very complex logic. And now we have to change it in order to introduce the new SuperWorker. Let's see the disadvantages:

我们假设Manager类非常复杂,包含非常复杂的逻辑。 现在我们必须改变它才能引入新的SuperWorker。 让我们看看缺点:

  • we have to change the Manager class (remember it is a complex one and this will involve time and effort to make the changes).

  • some of the current functionality from the manager class might be affected.

  • the unit testing should be redone.

  • 我们必须更改Manager类(记住它是一个复杂的类,这将需要时间和精力来进行更改)。

  • 管理器类中的某些当前功能可能会受到影响。

  • 应重新进行单元测试。

All those problems could take a lot of time to be solved and they might induce new errors in the old functionlity. The situation would be different if the application had been designed following the Dependency Inversion Principle. It means we design the manager class, an IWorker interface and the Worker class implementing the IWorker interface. When we need to add the SuperWorker class all we have to do is implement the IWorker interface for it. No additional changes in the existing classes.

所有这些问题可能需要花费大量时间才能解决,并且它们可能会在旧功能中引发新的错误。 如果应用程序是根据依赖性倒置原则设计的,情况会有所不同。 这意味着我们设计了管理器类,IWorker接口和实现IWorker接口的Worker类。 当我们需要添加SuperWorker类时,我们所要做的就是为它实现IWorker接口。 现有类中没有其他更改。

// Dependency Inversion Principle - Bad example

class Worker {

    public void work() {

        // ....working

    }

}



class Manager {
    Worker worker;

    public void setWorker(Worker w) {
        worker = w;
    }

    public void manage() {
        worker.work();
    }
}

class SuperWorker {
    public void work() {
        //.... working much more
    }
}

Below is the code which supports the Dependency Inversion Principle. In this new design a new abstraction layer is added through the IWorker Interface. Now the problems from the above code are solved(considering there is no change in the high level logic):

下面是支持依赖性倒置原则的代码。 在这个新设计中,通过IWorker接口添加了一个新的抽象层。 现在解决了上述代码中的问题(考虑到高级逻辑没有变化):

  • Manager class doesn't require changes when adding SuperWorkers.

  • Minimized risk to affect old functionality present in Manager class since we don't change it.

  • No need to redo the unit testing for Manager class.

  • 添加SuperWorkers时,Manager类不需要更改。

  • 最小化影响Manager类中存在的旧功能的风险,因为我们不更改它。

  • 无需重做Manager类的单元测试。

// Dependency Inversion Principle - Good example
interface IWorker {
    public void work();
}

class Worker implements IWorker{
    public void work() {
        // ....working
    }
}

class SuperWorker  implements IWorker{
    public void work() {
        //.... working much more
    }
}

class Manager {
    IWorker worker;

    public void setWorker(IWorker w) {
        worker = w;
    }

    public void manage() {
        worker.work();
    }
}

Conclusion

总结

When this principle is applied it means the high level classes are not working directly with low level classes, they are using interfaces as an abstract layer. In this case instantiation of new low level objects inside the high level classes(if necessary) can not be done using the operator new. Instead, some of the Creational design patterns can be used, such as Factory Method, Abstract Factory, Prototype.

当应用这个原则时,它意味着高级类不能直接使用低级类,它们使用接口作为抽象层。 在这种情况下,不能在高级类(如果需要)内实例化新的低级对象。 相反,可以使用一些Creational设计模式,例如Factory Method,Abstract Factory,Prototype。

The Template Design Pattern is an example where the DIP principle is applied.

模板设计模式是应用DIP原理的示例。

Of course, using this principle implies an increased effort, will result in more classes and interfaces to maintain, in a few words in more complex code, but more flexible. This principle should not be applied blindly for every class or every module. If we have a class functionality that is more likely to remain unchanged in the future there is not need to apply this principle.

当然,使用这个原则意味着增加了工作量,将导致更多的类和接口维护,用更简单的代码表示,但更灵活。 这个原则不应盲目地适用于每个类或每个模块。 如果我们的类功能在未来更有可能保持不变,则不需要应用此原则。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270