Dependency Inversion Principle - 依賴性倒置原則

原文鏈接:Dependency Inversion Principle

Motivation

動(dòng)機(jī)

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?

當(dāng)我們?cè)O(shè)計(jì)軟件應(yīng)用程序時(shí),我們可以認(rèn)為低級(jí)別類實(shí)現(xiàn)基本和主要操作(磁盤訪問(wèn),網(wǎng)絡(luò)協(xié)議......)和高級(jí)類,這些類封裝了復(fù)雜的邏輯(業(yè)務(wù)流,......)。 最后一個(gè)依賴于低級(jí)別的課程。 實(shí)現(xiàn)這種結(jié)構(gòu)的一種自然方式是編寫低級(jí)類,一旦我們讓它們編寫復(fù)雜的高級(jí)類。 由于高級(jí)類是根據(jù)其他類別定義的,因此這似乎是合乎邏輯的方法。 但這不是一個(gè)靈活的設(shè)計(jì)。 如果我們需要更換低級(jí)別課程會(huì)怎樣?

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.

我們來(lái)看一個(gè)復(fù)制模塊的經(jīng)典例子,它從鍵盤讀取字符并將它們寫入打印機(jī)設(shè)備。 包含邏輯的高級(jí)類是Copy類。 低級(jí)類是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).

在糟糕的設(shè)計(jì)中,高級(jí)類直接使用并且在很大程度上依賴于低級(jí)類。 在這種情況下,如果我們想要更改設(shè)計(jì)以將輸出定向到新的FileWriter類,我們必須在Copy類中進(jìn)行更改。 (我們假設(shè)它是一個(gè)非常復(fù)雜的類,有很多邏輯并且很難測(cè)試)。

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.

為了避免這些問(wèn)題,我們可以在高級(jí)類和低級(jí)類之間引入一個(gè)抽象層。 由于高級(jí)模塊包含復(fù)雜邏輯,因此它們不應(yīng)依賴于低級(jí)模塊,因此不應(yīng)基于低級(jí)模塊創(chuàng)建新的抽象層。 基于抽象層創(chuàng)建低級(jí)模塊。

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

根據(jù)這個(gè)原則,設(shè)計(jì)類結(jié)構(gòu)的方法是從高級(jí)模塊開始到低級(jí)模塊:
高級(jí)類 - >抽象層 - >低級(jí)類

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.

  • 高級(jí)模塊不應(yīng)該依賴于低級(jí)模塊。 兩者都應(yīng)該取決于抽象。

  • 抽象不應(yīng)取決于細(xì)節(jié)。 細(xì)節(jié)應(yīng)取決于抽象。

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.

以下是違反依賴性倒置原則的示例。 我們有一個(gè)高級(jí)類的經(jīng)理類,以及名為Worker的低級(jí)類。 我們需要在我們的應(yīng)用程序中添加一個(gè)新模塊,以模擬公司結(jié)構(gòu)的變化,這些變化取決于新專業(yè)人員的雇用。 我們?yōu)榇藙?chuàng)建了一個(gè)新的類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:

我們假設(shè)Manager類非常復(fù)雜,包含非常復(fù)雜的邏輯。 現(xiàn)在我們必須改變它才能引入新的SuperWorker。 讓我們看看缺點(diǎn):

  • 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類(記住它是一個(gè)復(fù)雜的類,這將需要時(shí)間和精力來(lái)進(jìn)行更改)。

  • 管理器類中的某些當(dāng)前功能可能會(huì)受到影響。

  • 應(yīng)重新進(jìn)行單元測(cè)試。

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.

所有這些問(wèn)題可能需要花費(fèi)大量時(shí)間才能解決,并且它們可能會(huì)在舊功能中引發(fā)新的錯(cuò)誤。 如果應(yīng)用程序是根據(jù)依賴性倒置原則設(shè)計(jì)的,情況會(huì)有所不同。 這意味著我們?cè)O(shè)計(jì)了管理器類,IWorker接口和實(shí)現(xiàn)IWorker接口的Worker類。 當(dāng)我們需要添加SuperWorker類時(shí),我們所要做的就是為它實(shí)現(xiàn)IWorker接口。 現(xiàn)有類中沒(méi)有其他更改。

// 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):

下面是支持依賴性倒置原則的代碼。 在這個(gè)新設(shè)計(jì)中,通過(guò)IWorker接口添加了一個(gè)新的抽象層。 現(xiàn)在解決了上述代碼中的問(wèn)題(考慮到高級(jí)邏輯沒(méi)有變化):

  • 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時(shí),Manager類不需要更改。

  • 最小化影響Manager類中存在的舊功能的風(fēng)險(xiǎn),因?yàn)槲覀儾桓乃?/p>

  • 無(wú)需重做Manager類的單元測(cè)試。

// 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

總結(jié)

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.

當(dāng)應(yīng)用這個(gè)原則時(shí),它意味著高級(jí)類不能直接使用低級(jí)類,它們使用接口作為抽象層。 在這種情況下,不能在高級(jí)類(如果需要)內(nèi)實(shí)例化新的低級(jí)對(duì)象。 相反,可以使用一些Creational設(shè)計(jì)模式,例如Factory Method,Abstract Factory,Prototype。

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

模板設(shè)計(jì)模式是應(yīng)用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.

當(dāng)然,使用這個(gè)原則意味著增加了工作量,將導(dǎo)致更多的類和接口維護(hù),用更簡(jiǎn)單的代碼表示,但更靈活。 這個(gè)原則不應(yīng)盲目地適用于每個(gè)類或每個(gè)模塊。 如果我們的類功能在未來(lái)更有可能保持不變,則不需要應(yīng)用此原則。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容