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閱讀 227,702評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,143評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,553評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,620評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,416評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,940評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,024評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,170評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,709評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,597評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,784評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,291評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,029評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,407評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,663評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,403評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,746評論 2 370