Resolver: Injection Strategies

Resolver: Injection Strategies翻譯

注入策略

使用 Resolver 執(zhí)行依賴(lài)項(xiàng)注入的主要方法有五種:

  1. 接口注入
  2. 屬性注入
  3. 構(gòu)造器注入
  4. 方法注入
  5. 服務(wù)定位器
  6. 注釋 (NEW)

名稱(chēng)和數(shù)量都來(lái)自于依賴(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ò)展將該接口注入到類(lèi)或?qū)ο笾小?/p>

類(lèi):
class XYZViewModel {

    lazy var fetcher: XYZFetching = getFetcher()
    lazy var service: XYZService = getService()

    func load() -> Data {
        return fetcher.getData(service)
    }

}
依賴(lài)注入的代碼:
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(),否則您將返回到緊密耦合依賴(lài)類(lèi)并繞過(guò)解析注冊(cè)系統(tǒng)。

優(yōu)點(diǎn):
  • 輕盈。
  • 對(duì)類(lèi)隱藏依賴(lài)注入系統(tǒng)。
  • 對(duì)于在初始化過(guò)程中沒(méi)有訪(fǎng)問(wèn)權(quán)限的類(lèi),如UIViewController,非常有用。
缺點(diǎn):
  • 為每個(gè)需要注入的服務(wù)編寫(xiě)訪(fǎng)問(wèn)器函數(shù)。

2. 屬性注入

定義

屬性注入將其依賴(lài)項(xiàng)公開(kāi)為屬性,依賴(lài)項(xiàng)注入系統(tǒng)需要確保在調(diào)用任何方法之前都已設(shè)置好。

類(lèi):
class XYZViewModel {

    var fetcher: XYZFetching!
    var service: XYZService!

    func load() -> Data {
        return fetcher.getData(service)
    }

}
依賴(lài)注入代碼
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)部組件公開(kāi)為公共變量。
  • 很難確保一個(gè)對(duì)象得到了它所需要的一切來(lái)完成它的工作。
  • 在注冊(cè)時(shí)還有更多的工作要做。

3.構(gòu)造器注入

定義

構(gòu)造函數(shù)是Swift初始值設(shè)定項(xiàng)的Java術(shù)語(yǔ),但其思想是相同的:通過(guò)初始化函數(shù)傳遞對(duì)象所需的所有依賴(lài)項(xiàng)。

類(lèi):
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)
   }

}
依賴(lài)注入代碼
func setupMyRegistrations {
    register { XYZViewModel(fetcher: resolve(), service: resolve()) }
    register { XYZFetcher() as XYZFetching }
    register { XYZService() }
}
優(yōu)點(diǎn):
  • 確保對(duì)象擁有完成其工作所需的一切,因?yàn)閷?duì)象不能以其他方式構(gòu)造。
  • 將依賴(lài)項(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ì)象注入到給定的方法中。

類(lèi):
class XYZViewModel {

    func load(fetcher: XYZFetching, service: XYZFetching) -> Data {
        return fetcher.getData(service)
    }

}
依賴(lài)注入代碼

你已經(jīng)看過(guò)了。在load函數(shù)中,服務(wù)對(duì)象被傳遞到fetcher的getData方法中。

優(yōu)點(diǎn):
  • 允許調(diào)用方動(dòng)態(tài)配置方法的行為。
  • 允許調(diào)用方構(gòu)造自己的行為并將其傳遞到方法中。
缺點(diǎn):
  • 將這些行為公開(kāi)給使用它的所有類(lèi)。
注意

在Swift中,將閉包傳遞到方法中也可以被視為方法注入的一種形式。

5.服務(wù)定位器

定義

服務(wù)定位器基本上是定位對(duì)象所需的資源和依賴(lài)項(xiàng)的服務(wù)。

從技術(shù)上講,服務(wù)定位器是它自己的設(shè)計(jì)模式,不同于依賴(lài)注入,但是解析器同時(shí)支持這兩種模式,并且當(dāng)支持視圖控制器和初始化過(guò)程不在您控制范圍內(nèi)的其他類(lèi)時(shí),服務(wù)定位器模式特別有用。(請(qǐng)參考故事板。)

類(lèi):
class XYZViewModel {

    var fetcher: XYZFetching = Resolver.resolve()
    var service: XYZService = Resolver.resolve()

    func load() -> Data {
        return fetcher.getData(service)
    }

}
依賴(lài)注入代碼:
func setupMyRegistrations {
    register { XYZFetcher() as XYZFetching }
    register { XYZService() }
}
優(yōu)點(diǎn):
  • 更少的代碼。
  • 對(duì)于在初始化過(guò)程中沒(méi)有訪(fǎng)問(wèn)權(quán)限的類(lèi),如UIViewController,非常有用。
缺點(diǎn):
  • 將依賴(lài)注入系統(tǒng)公開(kāi)給使用它的所有類(lèi)。

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)行依賴(lài)項(xiàng)注入。從swift5.1開(kāi)始,我們現(xiàn)在可以使用屬性包裝器執(zhí)行注釋。(請(qǐng)參考注釋。)

類(lèi):
class XYZViewModel {

    @Injected var fetcher: XYZFetching
    @Injected var service: XYZService

    func load() -> Data {
        return fetcher.getData(service)
    }

}
依賴(lài)注入代碼:
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ì)于在初始化過(guò)程中沒(méi)有訪(fǎng)問(wèn)權(quán)限的類(lèi),如UIViewController,非常有用。
缺點(diǎn):
  • 暴露了使用依賴(lài)注入系統(tǒng)的事實(shí)。

額外資源

這只是表面現(xiàn)象。要更深入地了解利弊,請(qǐng)參閱: Inversion of Control Containers and the Dependency Injection pattern ~ Martin Fowler

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

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

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