最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

swift依賴注入和依賴注入容器詳解

 更新時(shí)間:2023年01月28日 11:47:22   作者:獨(dú)孤星岳  
這篇文章主要為大家介紹了swift依賴注入和依賴注入容器詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

什么是控制反轉(zhuǎn)(Inversion of Control)?

控制反轉(zhuǎn)就是把傳統(tǒng)的控制邏輯委托給另一個(gè)類或框架來(lái)處理,客戶方只需實(shí)現(xiàn)具體的任務(wù)而不需要關(guān)心控制邏輯。

舉個(gè)例子,比如存在客戶方和服務(wù)方兩個(gè)類,客戶方需要調(diào)用服務(wù)方的函數(shù)來(lái)執(zhí)行某個(gè)邏輯。在傳統(tǒng)的編程方式中,客戶方根據(jù)自己的需求直接去調(diào)用服務(wù)方的函數(shù)從而達(dá)到目的。而控制反轉(zhuǎn),則是把控制邏輯交給服務(wù)方,服務(wù)方提供了一個(gè)控制流的框架,具體的內(nèi)容需要由客戶方來(lái)填充,也就是說(shuō)對(duì)流程的控制反轉(zhuǎn)了,現(xiàn)在是服務(wù)方調(diào)用客戶方。據(jù)說(shuō)好萊塢有句名言 Don't call us, we'll call you,差不多就是這個(gè)意思。以上的服務(wù)方也可以是庫(kù)代碼或者框架。

在 iOS 開(kāi)發(fā)中,有一個(gè)非常常見(jiàn)的控制反轉(zhuǎn)的實(shí)現(xiàn),可能很多人都沒(méi)有意識(shí)到這個(gè)就是控制反轉(zhuǎn),那就是 completionHandler,或者說(shuō) callback。

API.request(completion: { data in
    handleData(data)
})

在這個(gè)例子中,業(yè)務(wù)方只需要關(guān)系拿到數(shù)據(jù)以后干什么,而不關(guān)心 completion 的調(diào)用時(shí)機(jī),把 completion 的調(diào)用委托給了網(wǎng)絡(luò)庫(kù),這就是控制反轉(zhuǎn)。

控制反轉(zhuǎn)可以讓主要任務(wù)和控制邏輯分離,可以提升代碼的模塊性和擴(kuò)展性,讓代碼松耦合,并可以讓寫測(cè)試代碼變得簡(jiǎn)單。

常見(jiàn)的控制反轉(zhuǎn)的實(shí)現(xiàn)有:

  • 服務(wù)定 位 器(Service Locator)
  • 依賴注入
  • 上下文查找(Contextualized lookup)
  • 模板方法(Template method)
  • 策略模式(Strategy design pattern)

本文僅討論依賴注入這種實(shí)現(xiàn),暫不討論其他的實(shí)現(xiàn)。

什么是依賴注入?

依賴注入是控制反轉(zhuǎn)的一種具體實(shí)現(xiàn)。它在類的外部創(chuàng)建這個(gè)類的依賴對(duì)象,然后通過(guò)某個(gè)方式把這個(gè)依賴對(duì)象提供給類。通過(guò)依賴注入,把依賴對(duì)象的創(chuàng)建和綁定都移動(dòng)到了類的外部。

先看下面的例子:

class Car {
    var tyres: [Tyre]
    init() {
        let tyre1 = Tyre()
        let tyre2 = Tyre()
        let tyre3 = Tyre()
        let tyre4 = Tyre()
        tyres = [tyre1, tyre2, tyre3, tyre4]
    }
}

這個(gè)例子中構(gòu)建了一個(gè)汽車對(duì)象,汽車對(duì)象的構(gòu)建需要拼裝 4 個(gè)輪胎。這個(gè)代碼的缺點(diǎn)顯而易見(jiàn),就是輪胎的創(chuàng)建邏輯和汽車本身耦合了。當(dāng)我們想換成另一種輪胎時(shí),或者 Tyre 類調(diào)整了實(shí)現(xiàn)在構(gòu)造時(shí)添加了一個(gè)參數(shù),都必須改動(dòng) Car 類中的代碼。

用依賴注入的方式,把依賴對(duì)象的創(chuàng)建和綁定挪到類外部,就能解決這類問(wèn)題。

class Car {
    var tyres: [Tyre]
    init(types: [Tyre]) {
        self.types = types
    }
}

再舉個(gè)例子,App 開(kāi)發(fā)中常見(jiàn)的網(wǎng)絡(luò)請(qǐng)求 -> 數(shù)據(jù)處理 -> 數(shù)據(jù)渲染流程,傳統(tǒng)方式開(kāi)發(fā)如下:

// DataViewModel.swift
func loadData() {
    API.requestData(id: 2222, completion: { data in 
        self.handleData(data)
    })
}

這樣的代碼是無(wú)法測(cè)試的,因?yàn)?ViewModel 和具體的網(wǎng)絡(luò)請(qǐng)求實(shí)現(xiàn)耦合了。為了讓 loadData 這個(gè)方法可以被測(cè)試,應(yīng)該抽象一個(gè)網(wǎng)絡(luò)請(qǐng)求的接口,然后從外部注入這個(gè)接口的實(shí)現(xiàn)。如下代碼:

protocol Networking {
    func requestData(id: Int, completion: (Data) -> Void)
}

讓 DataViewModel 擁有一個(gè)需要注入的屬性對(duì)象:

class DataViewModel {
    let networking: Networking
    init(networking: Networking) {
        self.networking = networking
    }
}

loadData 方法修改如下:

func loadData(completion: (() -> Void)?) {
    networking.requestData(id: 2222, completion: { data in
        self.handleData(data)
    })
}

這樣把具體的網(wǎng)絡(luò)請(qǐng)求實(shí)現(xiàn)轉(zhuǎn)移到外部注入,在測(cè)試的時(shí)候就可以簡(jiǎn)單的實(shí)現(xiàn)一個(gè)模擬的網(wǎng)絡(luò)請(qǐng)求,這樣就可以寫出測(cè)試代碼:

func testLoadData() {
    let networking = MockNetworking()
    let viewModel = DataViewModel(networking: networking)
    let expectation = XCTestExpectation()
    viewModel.loadData {
        expectation.fulfill()
    }
    wait(for: [expectation], timeout: .infinity)
    XCTAssertTrue(viewModel.data.xxxx)
}

依賴注入最大的優(yōu)點(diǎn)就是實(shí)現(xiàn)了類之間的解耦。什么叫解耦,解耦就是兩個(gè)類之間雖然存在一些依賴關(guān)系,但是當(dāng)其中任何一個(gè)類的實(shí)現(xiàn)發(fā)生改變時(shí),另一個(gè)類的實(shí)現(xiàn)完全不受影響,解耦本身是通過(guò)抽象接口來(lái)實(shí)現(xiàn)的,因此依賴注入也需要抽象接口,并且依賴注入將依賴的創(chuàng)建轉(zhuǎn)移到了客戶類的外部,把依賴的創(chuàng)建邏輯也和客戶類本身解耦。這樣無(wú)論是替換依賴對(duì)象的實(shí)現(xiàn)類,還是替換依賴對(duì)象類實(shí)現(xiàn)中的某個(gè)部分,客戶方類都不需要做任何修改。

依賴注入的種類

依賴注入主要通過(guò)初始化器注入、屬性注入、方法注入、接口注入等方式來(lái)進(jìn)行注入。

初始化器注入

初始化器注入就是通過(guò)初始化方法的參數(shù)來(lái)給對(duì)象提供依賴。初始化器注入是最常用的注入方式,它簡(jiǎn)單直觀,當(dāng)一個(gè)對(duì)象依賴的對(duì)象的生命周期和對(duì)象自身相同時(shí),使用初始化器注入是最好的方式。

class ClientObject {
    private var dependencyObject: DependencyObject
    init(dependencyObject: DependencyObject) {
        self.dependencyObject = dependencyObject
    }
}

屬性注入

屬性注入是通過(guò)對(duì)象的公開(kāi)屬性來(lái)給對(duì)象提供依賴,也可以叫 setter 注入。一般在無(wú)法使用初始化器注入時(shí)(例如使用了 Storyboard),或者依賴對(duì)象的生命周期小于對(duì)象時(shí)使用。

public class ClientObject {
    public var dependencyObject: DependencyObject
}
let client = ClientObject()
client.dependencyObject = DefaultDependencyObject()

方法注入

方法注入的方式是對(duì)象需要實(shí)現(xiàn)一個(gè)接口,這個(gè)接口中聲明了能給對(duì)象提供依賴的方法。注入器通過(guò)調(diào)用這個(gè)方法來(lái)給對(duì)象提供依賴。方法注入也可以叫接口注入。

protocol DependencyObjectProvider {
    func dependencyObject() -> DependencyObject
}

有時(shí)客戶方只在某些特定的條件下才需要使用依賴,這時(shí)可以用方法注入,客戶方僅僅在需要使用依賴的時(shí)候才會(huì)去調(diào)用方法來(lái)創(chuàng)建依賴對(duì)象,這樣避免了客戶方在不使用依賴的時(shí)候依賴對(duì)象也被創(chuàng)建出來(lái)占用內(nèi)存空間的問(wèn)題。這一點(diǎn)也可以通過(guò)注入一個(gè)代碼塊來(lái)實(shí)現(xiàn):

init(dependencyBuilder: () -> DependencyObject) {
    self.dependencyBuilder = dependencyBuilder
}

依賴注入容器

依賴注入要求對(duì)象的依賴在對(duì)象外部創(chuàng)建并通過(guò)某種方式注入到對(duì)象中。如果每次創(chuàng)建對(duì)象時(shí)都去創(chuàng)建一遍對(duì)象的依賴,會(huì)讓代碼變得重復(fù)和復(fù)雜,當(dāng)對(duì)象的依賴調(diào)整時(shí),每個(gè)地方都需要做改動(dòng)。因此通常使用依賴注入時(shí),也需要一個(gè)依賴注入容器(Dependency Injection Container)。

依賴注入容器用來(lái)統(tǒng)一地管理依賴對(duì)象的創(chuàng)建和生命周期,也可以根據(jù)需要給對(duì)象注入依賴。

依賴注入容器要提供以下的功能:

  • 注冊(cè)(Register):容器需要知道對(duì)于一個(gè)特定的類型,應(yīng)該怎樣構(gòu)建它的依賴,容器內(nèi)會(huì)保存類型-依賴的映射信息,并提供接口可以向容器添加這個(gè)類型信息,這個(gè)操作就是注冊(cè)。
  • 解析(Resolve):當(dāng)使用依賴注入容器時(shí),就不需要手動(dòng)創(chuàng)建依賴了,而是讓容器幫我們做這件事情。容器需要提供一個(gè)方法來(lái)根據(jù)類型得到一個(gè)對(duì)象,容器會(huì)創(chuàng)建好這個(gè)對(duì)象的所有依賴,調(diào)用方即可無(wú)需關(guān)心依賴,直接使用這個(gè)對(duì)象即可。
  • 處置(Dispose):容器需要管理依賴對(duì)象的生命周期,并在依賴對(duì)象的生命周期結(jié)束時(shí)處置它。

實(shí)現(xiàn)一個(gè)簡(jiǎn)單的依賴注入容器

有很多第三方依賴注入框架實(shí)現(xiàn)了依賴注入的功能,例如 Swift 語(yǔ)言的 Swinject。我們也可以自己實(shí)現(xiàn)一個(gè)依賴注入容器。

按照依賴注入容器的定義,并借助 Swift 的泛型和協(xié)議,可以定義以下協(xié)議:

protocol DIContainer {
    func register<Component>(type: Component.Type, component: Any)
    func resolve<Component>(type: Component.Type) -> Component?
}

具體實(shí)現(xiàn)如下:

final class DefaultDIContainer: DIContainer {
    static let shared = DefaultDIContainer()
    private init() {}
    var components: [String: Any] = [:]
    func register<Component>(type: Component.Type, component: Any) {
        components["\(type)"] = component
    } 
    func resolve<Component>(type: Component.Type) -> Component? {
        return components["\(type)"] as? Component
    }
}

有了這個(gè) DIContainer,在使用時(shí)可以選擇兩種方式,一種是在外部 resolve 對(duì)象并注入。

let object = DIContaienr.shared.resolve(Data.self)
let viewModel = ViewModel(dependencyObject: object)

一種是在初始化方法的參數(shù)默認(rèn)值中 resolve:

class ViewModel {
    init(dependencyObject: DependencyObject = DIContainer.shared.resolve(Data.self))
    self.dependencyObject = dependencyObject
}

這兩種方式各有一些使用場(chǎng)景,可以根據(jù)具體情況選用。

以上的 DIContainer 只是一個(gè)簡(jiǎn)單的實(shí)現(xiàn),結(jié)合具體需求,可以添加上線程安全、Storyboard 注入,自動(dòng)解析等功能,可參考 Swinject。

總結(jié)

依賴注入在很多領(lǐng)域都是常見(jiàn)的設(shè)計(jì)模式,例如 Java Spring 等,無(wú)論做哪個(gè)方向的開(kāi)發(fā),依賴注入都是一定要掌握的。在合適的時(shí)候使用依賴注入,可以讓代碼解耦,提高代碼的可測(cè)試性、可擴(kuò)展性。

參考資料

以上就是swift依賴注入和依賴注入容器詳解的詳細(xì)內(nèi)容,更多關(guān)于swift依賴注入依賴注入容器的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Swift中圖片資源使用流程的優(yōu)化方法詳解

    Swift中圖片資源使用流程的優(yōu)化方法詳解

    這篇文章主要給大家介紹了關(guān)于Swift中圖片資源使用流程的優(yōu)化方法的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。
    2018-01-01
  • Swift無(wú)限循環(huán)控件開(kāi)發(fā)

    Swift無(wú)限循環(huán)控件開(kāi)發(fā)

    這篇文章主要為大家詳細(xì)介紹了Swift無(wú)限循環(huán)控件開(kāi)發(fā),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-07-07
  • Swift如何為設(shè)置中心添加常用功能

    Swift如何為設(shè)置中心添加常用功能

    這篇文章主要給大家介紹了關(guān)于Swift如何為設(shè)置中心添加常用功能的相關(guān)資料,包含了跳轉(zhuǎn)到AppStore、郵件反饋功能、系統(tǒng)分享功能以及打開(kāi)某些網(wǎng)址等功能,需要的朋友可以參考借鑒,下面來(lái)一起看看吧
    2018-05-05
  • Swift TableView實(shí)現(xiàn)凍結(jié)窗格功能

    Swift TableView實(shí)現(xiàn)凍結(jié)窗格功能

    這篇文章主要為大家詳細(xì)介紹了Swift TableView實(shí)現(xiàn)凍結(jié)窗格功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-11-11
  • 詳細(xì)講解Swift中的類型占位符

    詳細(xì)講解Swift中的類型占位符

    類型占位符是swift5.6中引?的?個(gè)新功能,在處理稍微復(fù)雜的通?類型時(shí),它可能真的很有用,這篇文章主要給大家介紹了關(guān)于Swift類型占位符的相關(guān)資料,需要的朋友可以參考下
    2022-06-06
  • Swift中定義二維數(shù)組的方法及遍歷方法示例

    Swift中定義二維數(shù)組的方法及遍歷方法示例

    這篇文章主要介紹了Swift中定義二維數(shù)組的方法及遍歷方法示例,本文直接給出代碼示例,需要的朋友可以參考下
    2015-07-07
  • Swift中的條件判斷、循環(huán)、跳轉(zhuǎn)語(yǔ)句基礎(chǔ)學(xué)習(xí)筆記

    Swift中的條件判斷、循環(huán)、跳轉(zhuǎn)語(yǔ)句基礎(chǔ)學(xué)習(xí)筆記

    if、for和while循環(huán)、switch等這些基本的程序流程控制語(yǔ)句基本上是每個(gè)編程語(yǔ)言的標(biāo)配,在入門環(huán)節(jié)中,這里對(duì)Swift中的條件判斷、循環(huán)、跳轉(zhuǎn)語(yǔ)句基礎(chǔ)學(xué)習(xí)筆記作了一個(gè)整理:
    2016-06-06
  • SwiftUI開(kāi)發(fā)總結(jié)combine原理簡(jiǎn)單示例詳解

    SwiftUI開(kāi)發(fā)總結(jié)combine原理簡(jiǎn)單示例詳解

    這篇文章主要為大家介紹了SwiftUI開(kāi)發(fā)總結(jié)combine原理簡(jiǎn)單示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02
  • Swift3.0仿支付寶二維碼掃描效果

    Swift3.0仿支付寶二維碼掃描效果

    這篇文章主要為大家詳細(xì)介紹了Swift3.0仿支付寶二維碼掃描效果的相關(guān)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-02-02
  • 利用Swift實(shí)現(xiàn)各類的CATransition動(dòng)畫詳解

    利用Swift實(shí)現(xiàn)各類的CATransition動(dòng)畫詳解

    CATransition動(dòng)畫主要在過(guò)渡時(shí)使用,比如兩個(gè)頁(yè)面層級(jí)改變的時(shí)候添加一個(gè)轉(zhuǎn)場(chǎng)效果。CATransition分為兩類,一類是公開(kāi)的動(dòng)畫效果,一類是非公開(kāi)的動(dòng)畫效果。這篇文章主要給大家介紹了關(guān)于如何利用Swift實(shí)現(xiàn)各類CATransition動(dòng)畫的相關(guān)資料,需要的朋友可以參考下。
    2017-09-09

最新評(píng)論

邻水| 砚山县| 陇川县| 盐津县| 泗阳县| 漳浦县| 新干县| 石棉县| 濮阳市| 广安市| 噶尔县| 探索| 仙游县| 棋牌| 三门县| 电白县| 宕昌县| 浑源县| 遂平县| 大足县| 刚察县| 林西县| 射洪县| 北宁市| 耿马| 利津县| 沙田区| 镇坪县| 佛冈县| 黄骅市| 贺州市| 九江市| 克拉玛依市| 天水市| 临澧县| 兰坪| 关岭| 南宁市| 千阳县| 台北市| 太仆寺旗|