依賴倒置原則(Dependence Inversion Principle)

狼爺發表於2021-05-16

背景

這幾天組內的人一起學習DDD,裡面再次提到了依賴倒置原則,在這學習過程中,大家又討論了一下依賴倒置原則。

說明

採用依賴倒置原則可以減少類間的耦合性,提高系統的穩定性,減少並行開發引起的風險,提高程式碼的可讀性和可維護性。

那麼依賴倒置原則是什麼呢?

高層次的模組不應該依賴於低層次的模組,他們都應該依賴於抽象。

抽象不應該依賴於具體,具體應該依賴於抽象。

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”所寫的專欄Engineering Notebook的第三篇,後來加入到他在2002年出版的經典著作“Agile Software Development, Principles,Parrerns,and Practices”一書中。

例子

下面是一個違反依賴倒置原則的示例。我們有高層類Manager,還有底層類Worker。現在我們需要在應用程式中增加一個新模組,以模擬由僱用新專業工人決定的公司結構變化,我們為此建立了一個新的類SuperWorker

讓我們假設Manager類相當複雜,包含非常複雜的邏輯。現在我們必須改變它,以引入新的SuperWorker。讓我們看看缺點:

  • 我們必須更改類Manager(記住這是一個複雜的類,這將涉及時間和精力進行更改)。
  • Manager類中的某些功能可能會受到影響。
  • 單元測試應重做。

所有這些問題都可能需要很多時間來解決,並且它們可能會在舊功能中誘發新的錯誤。如果應用程式是按照依賴倒置原則來設計的,情況就不一樣了。這意味著我們設計Manager類時,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
	}
}

以下是支援依賴性反轉原則的程式碼。在這個新設計中,通過IWorker 介面新增了一個新的抽象層。現在,上述程式碼中的問題已經解決(考慮到高層邏輯沒有變化):

  • 在新增SuperWorker時,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();
    }
}

當然,使用此原則意味著加大努力,將導致更多的類和介面來維護,換句話說,在更復雜的程式碼,但更靈活。不應盲目地將這一原則應用於每個類或每個模組。如果我們的類功能將來更有可能保持不變,則無需應用此原則。

“倒置”的解釋

《設計模式之禪》中對“倒置”的解釋:

講了這麼多,估計大家對“倒置”這個詞還是有點不理解,那到底什麼是“倒置”呢?我們先說“正置”是什麼意思,依賴正置就是類間的依賴是實實在在的實現類間的依賴,也就是面向實現程式設計,這也是正常人的思維方式,我要開賓士車就依賴賓士車,我要使用膝上型電腦就直接依賴膝上型電腦,而編寫程式需要的是對現實世界的事物進行抽象,抽象的結果就是有了抽象類和介面,然後我們根據系統設計的需要產生了抽象間的依賴,代替了人們傳統思維中的事物間的依賴,“倒置”就是從這裡產生。

可以這麼解釋:高層A依賴於低層B修改為高層A依賴於抽象層C,低層B依賴於抽象層C,而抽象層C是低層B的高層,所以說低層依賴於高層了,即倒置。但是A依賴於C,這個不能說是低層依賴於高層吧,A和C誰高誰低是這麼判定的?因為抽象層C是屬於A層的,即由A層來規定抽象層C的介面規範,而B是對C的實現,因此通過引入C層實現了“依賴倒置”。

總結

一句話:依賴倒置原則的核心就是面向抽象(抽象類或者介面)程式設計。

設計類結構的方法是從高層模組到底層模組:
高層類 --> 抽象層 --> 底層類

參考資料

相關文章