設計模式之依賴倒置原則

相關鏈接:
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 點:

  • 低層模塊盡量都要有抽象類或接口,或者兩者都有
  • 變量的聲明類型盡量是抽象類或接口
  • 使用繼承時遵循里式替換原則

依賴倒置原則的核心就是要我們面向接口編程,理解了面向接口編程,也就理解了依賴倒置。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容