本章主軸#

轉接模式(Adapter)的核心:有個物件做的事是對的、但介面不對。Adapter 為它套上一層,讓它能以期望的介面被使用。它在許多其他模式中都會被搭配使用,是極為常見的工具。

GoF 對 Adapter 的意圖描述#

把一個類別的介面轉換成 client 期望的另一個介面。Adapter 讓原本因介面不相容而無法合作的類別能夠協同工作。

一個啟發式範例:Shape 與 XXCircle#

設想需求:

  • 設計 Point、Line、Square 等類別,皆有 display 行為
  • Client 不應在意自己拿到的是哪種具體形狀

用繼承達成多型#

  • 抽出抽象類別 Shape,定義 displayfillundisplay 等方法
  • 讓 Point、Line、Square 各自繼承並實作

Figure 7-1: 我們有的(Points、Lines、Squares)對 Client 而言應該都是 Shape

Figure 7-3: 抽象 Shape 介面下的 Point、Line、Square 實作了 display、fill、undisplay 等方法

來了個新需求:加入圓形#

  • 同事 Jill 已經寫好 XXCircle,方法名為 displayItfillItundisplayIt

Figure 7-4: Jill 的 XXCircle 類別——名稱與參數都和 Shape 不一致

  • 但無法直接套用:
    • 方法名稱與參數不同
    • XXCircle 沒有繼承 Shape
  • 改 Jill 的程式既冒風險、又破壞她現有的使用者

解法:包一層 Adapter#

  • 建立 Circle 類別,繼承 Shape
  • Circle 內部持有一個 XXCircle
  • Circle.display() 內部委派給 XXCircle.displayIt()
class Circle extends Shape {
    private XXCircle myXXCircle;

    public Circle() {
        myXXCircle = new XXCircle();
    }

    public void display() {
        myXXCircle.displayIt();
    }
}

這樣便保留多型,又不必改動 XXCircle

Figure 7-6: Adapter 模式的通用結構——Adapter 包住 Adaptee 並符合 Target 介面

Adapter 的關鍵特性#

欄位內容
Intent把無法控制的既有物件,匹配到特定介面
Problem系統有正確的資料與行為,但介面不對
SolutionAdapter 提供一層具備所需介面的包裝
Consequences讓既有物件得以納入新類別結構,而不被原介面所限
Implementation把既有類別包進新類別,新類別實作所需介面、轉呼叫包進去的物件

Adapter 的兩種變體#

  • Object Adapter:透過「持有」既有物件來實作(前述例子)
  • Class Adapter:用多重繼承——public 繼承所需抽象類別、private 繼承既有類別

Object Adapter 與 Class Adapter 的取捨牽涉問題領域中的力量;GoF 書中第 142–144 頁有詳細討論。

部分功能不全的 Adapter#

當被包進來的物件只能滿足一部分需求時,仍可使用 Adapter:

  • 既有方法 → 透過 Adapter 轉發
  • 缺少的方法 → 在 Adapter 類別內自行實作

雖然不像「完美包裝」那樣優雅,但仍可避免從零實作整套功能。

Adapter vs Facade#

Figure 7-7: Client 透過介面使用既有但介面不對的物件

兩者乍看都是「包裝(wrapper)」,常被混淆:

比較項FacadeAdapter
是否有既有類別
是否要符合特定介面
是否需要多型行為通常是
是否提供更簡單介面不一定

重點差異:Facade 簡化 介面,Adapter 轉換 介面。

也常聽見人說「Facade 包多個類別、Adapter 只包一個」。雖然多數情況如此,但這並非模式的定義:Facade 可以包單一複雜物件,Adapter 也可以包多個小物件來合成一個介面。

與 CAD/CAM 問題的關聯#

V2 中的 OOGFeature 物件做的事是對的,但介面非作者設計,無法直接套用為 Feature 的子類別。Adapter 正是把 OOGFeature 包成符合 Feature 介面的最佳工具。

本章要記住的事#

  • Adapter 讓「正確但介面不對」的物件得以被納入既有結構
  • 它幾乎是其他模式(特別是 Bridge、Abstract Factory)必備的搭檔
  • 設計時可以先不必擔心既有類別的介面——後續 Adapter 永遠救得了
  • Facade 是「簡化」介面、Adapter 是「轉換」介面,別搞混