設(shè)計(jì)模式筆記及Swift上的實(shí)現(xiàn)之六『ADAPTER(適配器)』

什么是適配器?在真實(shí)世界中我們會接觸到各種各樣的適配器,例如上圖的電源適配器。將兩座的電源接口轉(zhuǎn)換為 USB 接口。

意圖

適配器模式則是將一個(gè)類的接口轉(zhuǎn)換成客戶希望的另外一個(gè)接口。 (=。= 這么看來適配器模式我們是一直都在用?。?/p>

動機(jī)

有時(shí),為了復(fù)用而設(shè)計(jì)的工具箱類,不能夠被復(fù)用的原因僅僅是因?yàn)樗慕涌谂c專業(yè)應(yīng)用領(lǐng)域所需要的接口不匹配。

例如,我們 iOS 開發(fā)中經(jīng)常會封裝一個(gè)網(wǎng)絡(luò)請求類,用于封裝一下固定的傳參等其他操作。我們平時(shí)進(jìn)行網(wǎng)絡(luò)請求也是調(diào)用這個(gè)類,而這個(gè)類就是一個(gè)適配器。這些固定的傳參操作只是我們這個(gè)項(xiàng)目要用到的,是專業(yè)應(yīng)用領(lǐng)域。

適用性

  • 你想使用一個(gè)已存在的類,而它的接口不符合你的需求。
  • 你想創(chuàng)建一個(gè)可以復(fù)用的類,該類可以與其他不相關(guān)的類或不可預(yù)見的類(那些接口不一定兼容的類)協(xié)調(diào)工作。
  • 你想使用一些已存在的子類,但是不可能對每一個(gè)都進(jìn)行子類化匹配他們的接口。對象適配器可以適配它的父類接口(僅適用于對象 Adapter)

結(jié)構(gòu)

類適配器使用多繼承對一個(gè)接口與另一個(gè)接口進(jìn)行匹配。

對象匹配器依賴于對象組合。

參與者

  • Target

定義 Client 使用的與特定領(lǐng)域相關(guān)的接口。

  • Client

與符合 Target 接口的對象協(xié)同。

  • Adaptee

定義一個(gè)已存在的接口,這個(gè)接口需要適配。

  • Adapter

對 Adaptee 的接口與 Target 接口進(jìn)行適配。

協(xié)助

Client 在 Adapter 示例上調(diào)用一下操作。 Adapter 調(diào)用 Adaptee 的操作實(shí)現(xiàn)請求。

效果

類適配器:

  • 用一個(gè)具體的 Adapter 類對 Adapter 和 Target 進(jìn)行匹配。當(dāng)我們想匹配一個(gè)類及它所有的子類時(shí),類 Adapter 將無法勝任工作。
  • 使得一個(gè) Adapter 可以重定義 Adaptee 的部分行為。
  • 引入一個(gè)對象,并不需要額外的指針以間接得到 Adaptee。(=。=這樣點(diǎn)現(xiàn)在估計(jì)不怎么考慮了吧)

對象適配器:

  • 允許一個(gè) Adapter 與多個(gè) Adaptee 同時(shí)工作。
  • 使得重定義 Adaptee 的行為比較困難。

使用 Adapter 模式時(shí)需要考慮的其他一些因素有:

  • Adaptee 的匹配程度
  • 可插入的 Adapter 當(dāng)其他的類使用一個(gè)類時(shí),如果所需的假定條件越少,這個(gè)類就更具有可復(fù)用性。
  • 使用雙向適配器提供透明操作 使用適配器的存在著一個(gè)潛在的問題,它們不對所有的客戶都透明。被適配的對象不在兼容 Adaptee 的接口,因此并不是所有的 Adaptee 對象可以被使用的地方,它都可以被使用。雙向適配器提供了這樣的透明性。

實(shí)現(xiàn)

  • 使用 C++ 實(shí)現(xiàn)適配器類 在 C++ 支持多繼承,這里需要使用多繼承,所以這里寫的是使用 C++ 實(shí)現(xiàn)適配器類。我認(rèn)為這里在 Swift 中其實(shí)可以通過 Protocol 來實(shí)現(xiàn),但前提是 Adaptee 是個(gè) Protocol。 Adapter 類繼承 Target 類,同時(shí) Adapter 私有繼承 Adaptee 類。
  • 使用抽象操作
  • 使用代理對象 這個(gè)中方式在 OC 的代碼中經(jīng)常會看到。 Adaptee 將一些操作提供給代理對象,這樣客戶可以對適配加以控制。
  • 參數(shù)化的適配器 從書中 Smalltask 的代碼理解,就是使用 Block 實(shí)現(xiàn)適配。

代碼示例

書中類適配器的實(shí)現(xiàn)更適合 C++, 所以我們的代碼示例就使用書中的對象適配器的實(shí)現(xiàn)。

Shape 假定有一個(gè)邊框,這個(gè)邊框由它相對的兩角定義。而 TextView 則有原點(diǎn)、寬、高的定義。TextShape 類是這些不同接口的適配器。

protocol Shape {
    
    func boundingBox() -> (bottomLeft: CGPoint, topRight: CGPoint)
    
    func createManipulator() -> Manipulator
    
}

extension Shape {
    
    func boundingBox() -> (bottomLeft: CGPoint, topRight: CGPoint) {
        return (CGPoint.zero, CGPoint.zero)
    }
    
    func createManipulator() -> Manipulator {
        return Manipulator()
    }
}

class TextView {
    func getOrigin() -> CGPoint {
        return CGPoint(x: 10, y: 10)
    }
    
    func getExtent() -> CGSize {
        return CGSize(width: 10, height: 10)
    }
    
    func isEmpty() -> Bool {
        return false
    }
}

我們采用對象適配器, TextShape 需要維護(hù)一個(gè) TextView 對象。

class TextShape: Shape {
    
    let textView: TextView
    
    init(_ textView: TextView) {
        self.textView = textView
    }
    
    func boundingBox() -> (bottomLeft: CGPoint, topRight: CGPoint) {
        
        let origin = textView.getOrigin()
        let extent = textView.getExtent()
        
        let bottom = origin.x
        let left = origin.y
        
        return (CGPoint(x: bottom, y: left), topRight: CGPoint(x: bottom + extent.height, y: left + extent.width))
    
    }
    
    func createManipulator() -> Manipulator {
        return Manipulator()
    }
    
    func isEmpty() -> Bool {
        return textView.isEmpty()
    }
    
}

總結(jié)

適配器分成兩種:類適配器和對象適配器。在 Swift 使用對象適配器,視乎更加合適。

附:Playground 代碼

歡迎討論、批評、指錯(cuò)。

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

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

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