1. 程式人生 > >依賴倒置原則(DIP)

依賴倒置原則(DIP)

依賴倒置原則

依賴倒置原則(Dependence Inversion Principle, DIP)原始定義是

High level modules should not depend upon low level modules.Both should depend upon abstractions.Abstractions should not depend upon details.Details should depend upon abstractions.

翻譯過來就是:

1、高層模組不應該依賴低層模組,兩者都應該依賴其抽象

2、抽象不應該依賴細節

3、細節應該依賴抽象

高層模組:通常只策略、業務場景

低層模組:也就是具體實現的細節

抽象:抽象就是指介面或抽象類,兩者都是不能直接被例項化的

細節:就是實現類,實現介面或繼承抽象類而產生的類就是細節

通俗一點:依賴倒置就是通過抽象(介面或抽象類)使各個類或者模組的實現彼此獨立,互不影響,實現模組間的鬆耦合。

依賴倒置原則在編碼中經常被使用,其核心思想就是面向介面程式設計,而不是面向實現程式設計。介面是指定義(規範、約束)與實現相分離,它是一種抽象,只要不修改介面宣告,那麼就可以放心使用,至於介面內部的實現無需關心。所以面向介面程式設計也可以簡單理解為面向協議程式設計,實現者按照協議來工作。理解了面向介面程式設計也就理解了依賴倒置。

問題描述

類A直接依賴於類B,假如要將類A改為依賴類C,則必須通過修改類A的程式碼來實現。這種場景下,類A一般是高層模組,負責複雜的業務邏輯;類B和類C是低層模組,負責基本的原子操作,假如修改類A,那麼會給程式帶來不必要的風險。

解決方案

將類A修改為依賴介面P,類B和類C都各自實現介面P,類A通過介面P間接與類B和類C發生聯絡,這樣能夠大大降低修改類A的機率。

Demo

師傅開車,我們有一個司機類,司機目前可以開賓士車,所以我們宣告類和方法如下:

// 司機
class Driver {
    func drive(_ benZ: BenZ) {
        benZ.run()
    }
}

// 賓士
class BenZ {
    func run() {
       print("benZ start to run...")
    }
}

程式碼非常簡單,司機開賓士車那是開的非常開心,但是這時候司機又想開寶馬了,或者司機想要開奧迪了,這該怎麼辦呢?目前我們的Driver類中的駕駛方法與BenZ類那是緊密耦合啊,司機想開其他車還真難,雖然我們可以為Driver類在新增新的方法來讓司機開寶馬,但是這樣其實是不友好的,重複程式碼太多,明明就是開車的功能,寫這麼多方法,顯然不對,為了解決這個問題,我們必須去除Driver類對賓士類的依賴,那麼就該使用DIP原則

1、宣告汽車協議

// 宣告汽車協議
protocol CarProtocol {
    func run()
}

2、讓賓士、寶馬都遵循開汽車協議

// 賓士
class BenZ: CarProtocol {
    func run() {
       print("benZ start to run...")
    }
}

// 寶馬
class BMW: CarProtocol {
    func run() {
        print("bmw start to run...")
    }
}

3、司機只需要暴露駕駛方法即可,依賴協議進行開車

// 司機
class Driver {
    func drive(_ car: CarProtocol) {
        car.run()
    }
}

 現在司機想開什麼車子就開什麼車子,如果想開奧迪,建立一個奧迪類,遵守開汽車協議,那麼就OK了

let driver = Driver()
// 開賓士
driver.drive(BenZ())
// 開寶馬
driver.drive(BMW())

如果想進一步優化,其實driver方法也應該宣告為介面,即協議方法,但是個人感覺目前其實OK了,符合我們所講的高層模組不應該依賴低層模組,兩者都應該依賴其抽象;抽象不應該依賴細節;細節應該依賴抽象

依賴倒置原則優點

1、可以減少類間的耦合性

2、提高系統的穩定性

3、降低並行開發引起的風險

4、提高程式碼的可讀性和可維護性

依賴注入

依賴是可以傳遞的,A物件依賴B物件,B物件又依賴C物件,C物件又依賴D。。。生生不息,依賴不止,但是記住一點:只要做到抽象依賴,即使是多層的依賴傳遞也是無所謂的。

依賴注入的方式,更多詳細細節看這裡

1、建構函式注入

class Driver {
    let car: CarProtocol

    init(_ car: CarProtocol) {
        self.car = car
    }

    func drive() {
        car.run()
    }
}

2、屬性注入

class Driver {
    var car: CarProtocol?
    func drive() {
        car?.run()
    }
}

3、方法注入

class Driver {
    func drive(_ car: CarProtocol) {
        car.run()
    }
}

參考