欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

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

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

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

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

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

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

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

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

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

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

什么是依賴注入?

依賴注入是控制反轉(zhuǎn)的一種具體實現(xiàn)。它在類的外部創(chuàng)建這個類的依賴對象,然后通過某個方式把這個依賴對象提供給類。通過依賴注入,把依賴對象的創(chuà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òu)建了一個汽車對象,汽車對象的構(gòu)建需要拼裝 4 個輪胎。這個代碼的缺點顯而易見,就是輪胎的創(chuàng)建邏輯和汽車本身耦合了。當(dāng)我們想換成另一種輪胎時,或者 Tyre 類調(diào)整了實現(xiàn)在構(gòu)造時添加了一個參數(shù),都必須改動 Car 類中的代碼。

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

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

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

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

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

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

讓 DataViewModel 擁有一個需要注入的屬性對象:

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ò)請求實現(xiàn)轉(zhuǎn)移到外部注入,在測試的時候就可以簡單的實現(xiàn)一個模擬的網(wǎng)絡(luò)請求,這樣就可以寫出測試代碼:

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)點就是實現(xiàn)了類之間的解耦。什么叫解耦,解耦就是兩個類之間雖然存在一些依賴關(guān)系,但是當(dāng)其中任何一個類的實現(xiàn)發(fā)生改變時,另一個類的實現(xiàn)完全不受影響,解耦本身是通過抽象接口來實現(xiàn)的,因此依賴注入也需要抽象接口,并且依賴注入將依賴的創(chuàng)建轉(zhuǎn)移到了客戶類的外部,把依賴的創(chuàng)建邏輯也和客戶類本身解耦。這樣無論是替換依賴對象的實現(xiàn)類,還是替換依賴對象類實現(xiàn)中的某個部分,客戶方類都不需要做任何修改。

依賴注入的種類

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

初始化器注入

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

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

屬性注入

屬性注入是通過對象的公開屬性來給對象提供依賴,也可以叫 setter 注入。一般在無法使用初始化器注入時(例如使用了 Storyboard),或者依賴對象的生命周期小于對象時使用。

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

方法注入

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

protocol DependencyObjectProvider {
    func dependencyObject() -> DependencyObject
}

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

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

依賴注入容器

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

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

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

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

實現(xiàn)一個簡單的依賴注入容器

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

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

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

具體實現(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
    }
}

有了這個 DIContainer,在使用時可以選擇兩種方式,一種是在外部 resolve 對象并注入。

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
}

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

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

總結(jié)

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

參考資料

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

相關(guān)文章

  • 簡陋的swift carthage copy-frameworks 輔助腳本代碼

    簡陋的swift carthage copy-frameworks 輔助腳本代碼

    下面小編就為大家分享一篇簡陋的swift carthage copy-frameworks 輔助腳本代碼,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-01-01
  • 升級到Swift 4.0可能遇到的坑總結(jié)

    升級到Swift 4.0可能遇到的坑總結(jié)

    這篇文章主要給大家介紹了關(guān)于升級到Swift 4.0可能遇到的坑的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用swift4具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-11-11
  • Swift教程之屬性詳解

    Swift教程之屬性詳解

    這篇文章主要介紹了Swift教程之屬性詳解,屬性是描述特定類、結(jié)構(gòu)或者枚舉的值,計算屬性存在于類、結(jié)構(gòu)與枚舉中,存儲屬性僅僅只在類與結(jié)構(gòu)中,需要的朋友可以參考下
    2015-01-01
  • 10個驚艷的Swift單行代碼

    10個驚艷的Swift單行代碼

    這篇文章主要為大家分享了10個驚艷的Swift單行代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-11-11
  • Swift項目中利用SWRevealViewController實現(xiàn)側(cè)滑菜單

    Swift項目中利用SWRevealViewController實現(xiàn)側(cè)滑菜單

    這篇文章主要介紹了Swift項目中利用SWRevealViewController實現(xiàn)側(cè)滑菜單,需要的朋友可以參考下
    2015-12-12
  • 詳解Swift編程中下標(biāo)的用法

    詳解Swift編程中下標(biāo)的用法

    這篇文章主要介紹了Swift編程中下標(biāo)的用法,是Swift入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下
    2015-11-11
  • Swift下使用UICollectionView 實現(xiàn)長按拖拽功能

    Swift下使用UICollectionView 實現(xiàn)長按拖拽功能

    拖拽排序是新聞類的App可以說是必有的交互設(shè)計,如今日頭條,網(wǎng)易新聞等。這篇文章主要介紹了Swift下使用UICollectionView 長按拖拽功能,需要的朋友可以參考下
    2017-03-03
  • 深入理解Swift中的變量與常量

    深入理解Swift中的變量與常量

    本文主要是介紹Swift中最常用的常量和變量,將從“變量常量的定義”、"如何聲明變量常量"、“變量和常量的命名”,"變量常量的本質(zhì)區(qū)別"四個方面入手,重點介紹變量和常量的使用以及區(qū)別,希望大家在閱讀完本文后都可以熟練使用它們。有需要的朋友們下面來一起學(xué)習(xí)吧。
    2017-01-01
  • 舉例講解Swift編程中switch...case語句的用法

    舉例講解Swift編程中switch...case語句的用法

    這篇文章主要介紹了Swift編程中switch...case語句的用法,其中fallthrough關(guān)鍵字在switch語句中的使用是重點,需要的朋友可以參考下
    2016-04-04
  • Swift 圖表使用Foudation庫中測量類型詳解

    Swift 圖表使用Foudation庫中測量類型詳解

    這篇文章主要為大家介紹了Swift 圖表使用Foudation庫中測量類型詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10

最新評論