背景
這幾天組內的人一起學習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層實現了“依賴倒置”。
總結
一句話:依賴倒置原則的核心就是面向抽象(抽象類或者介面)程式設計。
設計類結構的方法是從高層模組到底層模組:
高層類 --> 抽象層 --> 底層類
參考資料
- 《Agile Software Development: Principles, Patterns, and Practices》 Robert C·Martin
- 《實現領域驅動設計》 Vaughn Vernon
- https://www.oodesign.com/dependency-inversion-principle.html
- https://q.cnblogs.com/q/72496/