上一篇,我介紹了 Facade 模式,今天我們繼續介紹另一個設計模式 —— Adapter 模式。
假設客戶給出以下需求:
display 與 undisplay 行為的點、線、正方形分別建立類別;那麼,我們在這個時候會希望使用多型 —— 具有同樣的 method(display, undisplay),在不同物件會有不同的行為。但是客戶與他們的對話會是共同的。若以 UML 圖來看,會如同下圖:

這樣子,在我們的 Client 眼中,他互動的是 Shape,不需要去知道自己有的是哪一種形狀。
今天,客戶需求增加了,客戶需要有個圓形,而且它也需要有 display 與 undisplay 兩種行為。
然而,你發現你同事在開發其他功能的時候,已經實作了一個名叫 MyCircle 類別,而此類別中剛好有 show 與 vanish 兩種 methods:他們所做的行為剛好就符合 display 和 undisplay 的需求。
可惜地是,這個 MyCircle 並沒有繼承於 Shape,意指上述的架構無法套用在 MyCircle 上。
你當然可以假裝沒看到我的圓,直接實作出你自己的 Circle 類別,但是這樣等於是出現兩組類似的程式碼在你的系統裡。

GoF 如此開示:
(Adapter 模式)將一個類別的介面轉換成客戶希望的另一個介面。Adapter 模式使原本由於介面不相容而不能一起工作的類別可以一起工作。
意思就是,某類別的功能符合我們的需求,但是介面上不同於原本的設計,那麼我們就建立一個新介面重新包裝該類別。
那我們如何實作 Adapter 模式在上述的問題上呢?
其實很簡單,就是建立一個 Circle 物件,然後將 Circle 包含 MyCircle。再將往後發自 Circle 的請求都丟給 MyCircle 去實作。
用 UML 圖來看,就會如下圖所示。

我們用 java 來實作,就會如下:
class Circle extends Shape {
...
private MyCircle myCircle;
...
public Circle() {
myCircle = new MyCircle();
}
void public display() {
myCircle.show();
}
void public undisplay() {
myCircle.vanish();
}
}
(終於有程式碼出現了,感動落淚)
如此一來,我們就能維持原本的架構:客戶不知道實際的形狀。Adapter 模式最常見的用途也就是在此 —— 維持多型。而有了 Adapter 模式,我們也不需要擔心原有類別的介面了,因為總是可以用 Adpter 模式來提供正確的介面。
| 項目 | 描述 |
|---|---|
| 意圖 | 使控制範圍外的原有物件與某個介面匹配 |
| 問題 | 系統資料、行為皆正確,只有介面不符。常用於必須從抽象類別衍生時 |
| 解決方案 | 提供具有所需介面的 wrapper(包裝)類別 |
| 參與者與協作者 | Adapter 改變了 Adaptee 的介面,使 Adaptee 與 Adapter 的父類別 Target 匹配。如此 Client 就可以使用 Adaptee 了,好像它是 Target 類型。 |
| 效果 | 使原有物件能夠適應新的類別結構,不受介面的限制 |
| 實作 | 把原有類別包含在另一個類別中。讓包含類別與需要的介面匹配。呼叫被複合類別的方法 |
下圖為 Adapter 模式的 UML 圖:

實際上 Adapter 模式有兩種類型:
類別 Adapter 模式的實現方法是建立新類別,該類別同時從兩種類別繼承:
兩模式的差異如下表:
| Facade 模式 | Adapter 模式 | |
|---|---|---|
| 是否存在既有的類別? | 是 | 是 |
| 是否需要依某個介面設計? | 否 | 是 |
| 物件是否需要多型行為? | 否 | 可能 |
| 需要更簡單的見面嗎? | 是 | 否 |
簡言之,Facade 模式簡化了介面;Adapter 模式則將一個介面轉成另一個介面。
接下來 cover 的是第八章。我們將會再從介紹設計模式跳出來,再回頭討論一下物件導向設計,並且引進新的觀點。
雖然好像又有點偏離主題(設計模式),但我認為這是很精彩的一章,這一章讓我重新看待了很多原本在物件導向中習以為常的事。所以我還是希望花點篇幅討論一下。
那麼明天見囉!