相關鏈接:
0. 設計模式之六大原則總結
1. 設計模式之單一職責原則
2. 設計模式之里式替換原則
3. 設計模式之依賴倒置原則
4. 設計模式之接口隔離原則
5. 設計模式之迪米特法則
6. 設計模式之開閉原則
1.1 定義
- 高層模塊不應該依賴低層模塊,二者都應該依賴其抽象;
- 抽象不應該依賴細節(jié);
- 細節(jié)應該依賴抽象
1.2 問題由來
類 A 直接依賴類 B,假如要將類 A 改為依賴類 C ,則必須通過修改類 A 的代碼來達成。這種場景下,類 A一般是高層模塊,負責復雜的業(yè)務邏輯;類 B 和類 C 是低層模塊,負責基本的原子操作;假如修改類 A,會給程序帶來不必要的風險。
1.3 解決方案
將類 A 修改為依賴接口 Interface1,類 B 和類 C 各自實現(xiàn)接口 Interface2,類 A 通過接口 Interface1 間接與類 B 或者類 C 發(fā)生聯(lián)系,則會大大降低修改類 A 的幾率。
1.4 具體分析
依賴倒置原則基于這樣一個事實:相對于細節(jié)的多變性,抽象的東西要穩(wěn)定的多。以抽象為基礎搭建起來的架構比以細節(jié)為基礎搭建起來的架構要穩(wěn)定的多。抽象指的是接口或者抽象類,細節(jié)就是具體的實現(xiàn)類,(iOS 中可以理解為抽象就是協(xié)議,細節(jié)是實現(xiàn)該協(xié)議的實現(xiàn)類),使用接口或者抽象類的目的是制定好規(guī)范和契約,而不去涉及任何具體的操作,把展現(xiàn)細節(jié)的任務交給他們的實現(xiàn)類去完成。
依賴倒置原則的核心思想是面向接口編程,達到解耦的過程。
1.5 舉例說明
我們用一個例子來說明面向接口編程相對于面向?qū)崿F(xiàn)編程好在什么地方。母親給孩子講故事,只要給她一本書,她就可以照著書給孩子講故事了,代碼如下:
class Book : NSObject{
func getContent() -> String {
return "在很久很久之前....";
}
}
class Mother : NSObject {
func narrate(book : Book) {
print("媽媽開始將故事");
print(book.getContent());
}
}
// 執(zhí)行的代碼
let mother = Mother()
mother.narrate(book: Book())
運行結果:
媽媽開始講故事
很久很久以前有一個阿拉伯的故事……
1.6 舉例進階
假如有一天,孩子長大了,需要了解國家大事了,不是看書,而是看報紙了,讓這位母親講一下報紙的新聞,報紙的代碼如下:
class Newspaper : NSObject{
func getContent() -> String {
return "2020 年的春運已經(jīng)開始了....";
}
}
但是,目前來說,這位母親辦不到啊,因為 Mother 類里面只有一個narrate方法,參數(shù)是Book,這時,還得需要母親學習如何讀報紙(修改 Mother 類的代碼,添加方法),需要修改 Mother 才能讀,但是實際上都是文字啊,Mother 再去學習,學啥???
假如以后需求變成雜志、網(wǎng)頁呢?還得需要不斷的修改 Mother,這顯然不是好的設計。原因就是 Mohter與 Book 之間的耦合性太高了,必須降低他們之間的耦合度才行。
我們以 iOS 為例,定義一個協(xié)議:讀物,只要是可以讀的都屬于讀物:
protocol IReader {
// 獲取讀物內(nèi)容
func getContent() -> String;
}
Mother類和接口 IReader 之間發(fā)生依賴關系,而 Book 和 Newspaper 都屬于讀物的范疇,他們各自都去實現(xiàn) IReader 接口,這樣就符合依賴倒置原則了,代碼修改為:
class Book : NSObject, IReader{
func getContent() -> String {
return "在很久很久之前....";
}
}
class Newspaper : NSObject, IReader{
func getContent() -> String {
return "2020 年的春運已經(jīng)開始了....";
}
}
class Mother : NSObject {
func narrate(reader : IReader) {
print("媽媽開始將故事");
print(reader.getContent());
}
}
1.7 舉例總結
這樣修改之后,無論以后想去看網(wǎng)頁、雜志,都不需要再修改 Mother 類了。這只是一個簡單的例子,實際情況中,代表高層模塊的 Mother 類將負責完成主要的業(yè)務邏輯,一旦需要對它進行修改,引入錯誤的風險極大。所以遵循依賴倒置原則可以降低類之前的耦合性,提高系統(tǒng)的穩(wěn)定性,降低修改程序造成的風險。
1.8 總結
采用依賴倒置原則給多人并行開發(fā)帶來了極大的便利,比如上例中,原本 Mother 類與 Book 類直接耦合時,Mother 類必須等 Book 類編碼完成后才可以進行編碼,因為 Mother 類依賴于 Book 類,修改后的程序則可以同時開工,互不影響,因為 Mother 與 Book 類之間一點關系也沒有。參與協(xié)作開發(fā)的人越多、項目越龐大,采用依賴倒置原則的意義就越重大。
在實際編程時,我們一般需要做到如下 3 點:
- 低層模塊盡量都要有抽象類或接口,或者兩者都有
- 變量的聲明類型盡量是抽象類或接口
- 使用繼承時遵循里式替換原則
依賴倒置原則的核心就是要我們面向接口編程,理解了面向接口編程,也就理解了依賴倒置。