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

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

意圖

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

動機

有時,為了復用而設計的工具箱類,不能夠被復用的原因僅僅是因為它的接口與專業(yè)應用領域所需要的接口不匹配。

例如,我們 iOS 開發(fā)中經常會封裝一個網絡請求類,用于封裝一下固定的傳參等其他操作。我們平時進行網絡請求也是調用這個類,而這個類就是一個適配器。這些固定的傳參操作只是我們這個項目要用到的,是專業(yè)應用領域。

適用性

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

結構

類適配器使用多繼承對一個接口與另一個接口進行匹配。

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

參與者

  • Target

定義 Client 使用的與特定領域相關的接口。

  • Client

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

  • Adaptee

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

  • Adapter

對 Adaptee 的接口與 Target 接口進行適配。

協(xié)助

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

效果

類適配器:

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

對象適配器:

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

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

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

實現(xiàn)

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

代碼示例

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

Shape 假定有一個邊框,這個邊框由它相對的兩角定義。而 TextView 則有原點、寬、高的定義。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 需要維護一個 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()
    }
    
}

總結

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

附:Playground 代碼

歡迎討論、批評、指錯。

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

相關閱讀更多精彩內容

友情鏈接更多精彩內容