Resolver: Injection Strategies翻譯
注入策略
使用 Resolver 執(zhí)行依賴項(xiàng)注入的主要方法有五種:
名稱和數(shù)量都來(lái)自于依賴反轉(zhuǎn)绎谦,有關(guān)更深入的討論屎开,請(qǐng)參考 Martin Fowler.
Here I'll simply provide a brief description and an example of implementing each using Resolver.
在這里赡若,我將簡(jiǎn)單地描述下每種策略顿仇,并提供 Resolver 實(shí)現(xiàn)對(duì)應(yīng)策略的例子秤茅。
1. 接口注入
定義
第一種注入技術(shù)是為注入定義一個(gè)接口璧亚,并使用Swift擴(kuò)展將該接口注入到類或?qū)ο笾小?/p>
類:
class XYZViewModel {
lazy var fetcher: XYZFetching = getFetcher()
lazy var service: XYZService = getService()
func load() -> Data {
return fetcher.getData(service)
}
}
依賴注入的代碼:
extension XYZViewModel: Resolving {
func getFetcher() -> XYZFetching { return resolver.resolve() }
func getService() -> XYZService { return resolver.resolve() }
}
func setupMyRegistrations {
register { XYZFetcher() as XYZFetching }
register { XYZService() }
}
請(qǐng)注意砰识,仍需要在getFetcher() 和getService() 中調(diào)用resolve()谴供,否則您將返回到緊密耦合依賴類并繞過解析注冊(cè)系統(tǒng)块茁。
優(yōu)點(diǎn):
- 輕盈。
- 對(duì)類隱藏依賴注入系統(tǒng)桂肌。
- 對(duì)于在初始化過程中沒有訪問權(quán)限的類数焊,如UIViewController,非常有用崎场。
缺點(diǎn):
- 為每個(gè)需要注入的服務(wù)編寫訪問器函數(shù)佩耳。
2. 屬性注入
定義
屬性注入將其依賴項(xiàng)公開為屬性,依賴項(xiàng)注入系統(tǒng)需要確保在調(diào)用任何方法之前都已設(shè)置好谭跨。
類:
class XYZViewModel {
var fetcher: XYZFetching!
var service: XYZService!
func load() -> Data {
return fetcher.getData(service)
}
}
依賴注入代碼
func setupMyRegistrations {
register { XYZViewModel() }
.resolveProperties { (resolver, model) in
model.fetcher = resolver.optional() // Note property is an ImplicitlyUnwrappedOptional
model.service = resolver.optional() // Ditto
}
}
func setupMyRegistrations {
register { XYZFetcher() as XYZFetching }
register { XYZService() }
}
優(yōu)點(diǎn):
- 簡(jiǎn)潔干厚。
- 也相當(dāng)輕李滴。
缺點(diǎn):
- 將內(nèi)部組件公開為公共變量。
- 很難確保一個(gè)對(duì)象得到了它所需要的一切來(lái)完成它的工作蛮瞄。
- 在注冊(cè)時(shí)還有更多的工作要做所坯。
3.構(gòu)造器注入
定義
構(gòu)造函數(shù)是Swift初始值設(shè)定項(xiàng)的Java術(shù)語(yǔ),但其思想是相同的:通過初始化函數(shù)傳遞對(duì)象所需的所有依賴項(xiàng)挂捅。
類:
class XYZViewModel {
private var fetcher: XYZFetching
private var service: XYZService
init(fetcher: XYZFetching, service: XYZService) {
self.fetcher = fetcher
self.service = service
}
func load() -> Image {
let data = fetcher.getData(token)
return service.decompress(data)
}
}
依賴注入代碼
func setupMyRegistrations {
register { XYZViewModel(fetcher: resolve(), service: resolve()) }
register { XYZFetcher() as XYZFetching }
register { XYZService() }
}
優(yōu)點(diǎn):
- 確保對(duì)象擁有完成其工作所需的一切包竹,因?yàn)閷?duì)象不能以其他方式構(gòu)造。
- 將依賴項(xiàng)隱藏為私有或內(nèi)部籍凝。
- 注冊(cè)時(shí)需要的代碼更少周瞎。
缺點(diǎn):
- 要求對(duì)象具有具有所需所有參數(shù)的初始值設(shè)定項(xiàng)。
- 在對(duì)象初始值設(shè)定項(xiàng)中需要更多的樣板代碼來(lái)將參數(shù)轉(zhuǎn)換為對(duì)象屬性饵蒂。
4.方法注入
定義
它不是一個(gè)模式声诸,而是直接使用解析器,羅列出來(lái)以供選擇退盯。
就像它的名字一樣彼乌,是將所需的對(duì)象注入到給定的方法中。
類:
class XYZViewModel {
func load(fetcher: XYZFetching, service: XYZFetching) -> Data {
return fetcher.getData(service)
}
}
依賴注入代碼
你已經(jīng)看過了渊迁。在load函數(shù)中慰照,服務(wù)對(duì)象被傳遞到fetcher的getData方法中。
優(yōu)點(diǎn):
- 允許調(diào)用方動(dòng)態(tài)配置方法的行為琉朽。
- 允許調(diào)用方構(gòu)造自己的行為并將其傳遞到方法中毒租。
缺點(diǎn):
- 將這些行為公開給使用它的所有類。
注意
在Swift中箱叁,將閉包傳遞到方法中也可以被視為方法注入的一種形式墅垮。
5.服務(wù)定位器
定義
服務(wù)定位器基本上是定位對(duì)象所需的資源和依賴項(xiàng)的服務(wù)。
從技術(shù)上講耕漱,服務(wù)定位器是它自己的設(shè)計(jì)模式算色,不同于依賴注入,但是解析器同時(shí)支持這兩種模式螟够,并且當(dāng)支持視圖控制器和初始化過程不在您控制范圍內(nèi)的其他類時(shí)灾梦,服務(wù)定位器模式特別有用。(請(qǐng)參考故事板妓笙。)
類:
class XYZViewModel {
var fetcher: XYZFetching = Resolver.resolve()
var service: XYZService = Resolver.resolve()
func load() -> Data {
return fetcher.getData(service)
}
}
依賴注入代碼:
func setupMyRegistrations {
register { XYZFetcher() as XYZFetching }
register { XYZService() }
}
優(yōu)點(diǎn):
- 更少的代碼若河。
- 對(duì)于在初始化過程中沒有訪問權(quán)限的類,如UIViewController给郊,非常有用牡肉。
缺點(diǎn):
- 將依賴注入系統(tǒng)公開給使用它的所有類。
6.注釋
定義
Annotation uses comments or other metadata to indication that dependency injection is required. As of Swift 5.1, we can now perform annotation using Property Wrappers. (See Annotation.)
Annotation使用注釋或其他元數(shù)據(jù)來(lái)指示需要進(jìn)行依賴項(xiàng)注入淆九。從swift5.1開始统锤,我們現(xiàn)在可以使用屬性包裝器執(zhí)行注釋毛俏。(請(qǐng)參考注釋。)
類:
class XYZViewModel {
@Injected var fetcher: XYZFetching
@Injected var service: XYZService
func load() -> Data {
return fetcher.getData(service)
}
}
依賴注入代碼:
func setupMyRegistrations {
register { XYZFetcher() as XYZFetching }
register { XYZService() }
}
優(yōu)點(diǎn):
- 更少的代碼饲窿。
- 隱藏注冊(cè)系統(tǒng)的細(xì)節(jié)煌寇,可以很容易地創(chuàng)建一個(gè)注入的屬性包裝器來(lái)支持任何DI系統(tǒng)。
- 對(duì)于在初始化過程中沒有訪問權(quán)限的類逾雄,如UIViewController阀溶,非常有用。
缺點(diǎn):
- 暴露了使用依賴注入系統(tǒng)的事實(shí)鸦泳。
額外資源
這只是表面現(xiàn)象银锻。要更深入地了解利弊,請(qǐng)參閱: Inversion of Control Containers and the Dependency Injection pattern ~ Martin Fowler