
什么是適配器?在真實世界中我們會接觸到各種各樣的適配器,例如上圖的電源適配器。將兩座的電源接口轉換為 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 使用對象適配器,視乎更加合適。
歡迎討論、批評、指錯。