[TOC]

背景
這幾天組內(nèi)的人一起學(xué)習(xí)DDD,里面再次提到了依賴倒置原則,在這學(xué)習(xí)過(guò)程中,大家又討論了一下依賴倒置原則。
說(shuō)明
采用依賴倒置原則可以減少類間的耦合性,提高系統(tǒng)的穩(wěn)定性,減少并行開(kāi)發(fā)引起的風(fēng)險(xiǎn),提高代碼的可讀性和可維護(hù)性。
那么依賴倒置原則是什么呢?
高層次的模塊不應(yīng)該依賴于低層次的模塊,他們都應(yīng)該依賴于抽象。
抽象不應(yīng)該依賴于具體,具體應(yīng)該依賴于抽象。
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.
依賴倒置原則是Robert C. Martin在1996年為“C++Reporter”所寫(xiě)的專欄Engineering Notebook的第三篇,后來(lái)加入到他在2002年出版的經(jīng)典著作“Agile Software Development, Principles,Parrerns,and Practices”一書(shū)中。
例子
下面是一個(gè)違反依賴倒置原則的示例。我們有高層類Manager,還有底層類Worker?,F(xiàn)在我們需要在應(yīng)用程序中增加一個(gè)新模塊,以模擬由雇用新專業(yè)工人決定的公司結(jié)構(gòu)變化,我們?yōu)榇藙?chuàng)建了一個(gè)新的類SuperWorker。
讓我們假設(shè)Manager類相當(dāng)復(fù)雜,包含非常復(fù)雜的邏輯?,F(xiàn)在我們必須改變它,以引入新的SuperWorker。讓我們看看缺點(diǎn):
- 我們必須更改類
Manager(記住這是一個(gè)復(fù)雜的類,這將涉及時(shí)間和精力進(jìn)行更改)。 -
Manager類中的某些功能可能會(huì)受到影響。 - 單元測(cè)試應(yīng)重做。
所有這些問(wèn)題都可能需要很多時(shí)間來(lái)解決,并且它們可能會(huì)在舊功能中誘發(fā)新的錯(cuò)誤。如果應(yīng)用程序是按照依賴倒置原則來(lái)設(shè)計(jì)的,情況就不一樣了。這意味著我們?cè)O(shè)計(jì)Manager類時(shí),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
}
}
以下是支持依賴性反轉(zhuǎn)原則的代碼。在這個(gè)新設(shè)計(jì)中,通過(guò)IWorker 界面添加了一個(gè)新的抽象層。現(xiàn)在,上述代碼中的問(wèn)題已經(jīng)解決(考慮到高層邏輯沒(méi)有變化):
- 在添加
SuperWorker時(shí),Manager類不需要更改。 - 由于我們不會(huì)改變它,因此最大限度地降低影響
Manager器類中當(dāng)前舊功能的風(fēng)險(xiǎn)。 - 無(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();
}
}
當(dāng)然,使用此原則意味著加大努力,將導(dǎo)致更多的類和接口來(lái)維護(hù),換句話說(shuō),在更復(fù)雜的代碼,但更靈活。不應(yīng)盲目地將這一原則應(yīng)用于每個(gè)類或每個(gè)模塊。如果我們的類功能將來(lái)更有可能保持不變,則無(wú)需應(yīng)用此原則。
“倒置”的解釋
《設(shè)計(jì)模式之禪》中對(duì)“倒置”的解釋:
講了這么多,估計(jì)大家對(duì)“倒置”這個(gè)詞還是有點(diǎn)不理解,那到底什么是“倒置”呢?我們先說(shuō)“正置”是什么意思,依賴正置就是類間的依賴是實(shí)實(shí)在在的實(shí)現(xiàn)類間的依賴,也就是面向?qū)崿F(xiàn)編程,這也是正常人的思維方式,我要開(kāi)奔馳車就依賴奔馳車,我要使用筆記本電腦就直接依賴筆記本電腦,而編寫(xiě)程序需要的是對(duì)現(xiàn)實(shí)世界的事物進(jìn)行抽象,抽象的結(jié)果就是有了抽象類和接口,然后我們根據(jù)系統(tǒng)設(shè)計(jì)的需要產(chǎn)生了抽象間的依賴,代替了人們傳統(tǒng)思維中的事物間的依賴,“倒置”就是從這里產(chǎn)生。

可以這么解釋:高層A依賴于低層B修改為高層A依賴于抽象層C,低層B依賴于抽象層C,而抽象層C是低層B的高層,所以說(shuō)低層依賴于高層了,即倒置。但是A依賴于C,這個(gè)不能說(shuō)是低層依賴于高層吧,A和C誰(shuí)高誰(shuí)低是這么判定的?因?yàn)槌橄髮覥是屬于A層的,即由A層來(lái)規(guī)定抽象層C的接口規(guī)范,而B(niǎo)是對(duì)C的實(shí)現(xiàn),因此通過(guò)引入C層實(shí)現(xiàn)了“依賴倒置”。
總結(jié)
一句話:依賴倒置原則的核心就是面向抽象(抽象類或者接口)編程。
設(shè)計(jì)類結(jié)構(gòu)的方法是從高層模塊到底層模塊:
高層類 --> 抽象層 --> 底層類
參考資料
- 《Agile Software Development: Principles, Patterns, and Practices》 Robert C·Martin
- 《實(shí)現(xiàn)領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)》 Vaughn Vernon
- https://www.oodesign.com/dependency-inversion-principle.html