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