依賴倒置原則(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()
}
}
參考