今天來講下Adapter跟Proxy 這2個除了定義不太一樣以外
其實我在實作的時候它們都是在解決差不多的問題
就是將不相容的程式組合再一起 供使用者使用 但區別在實體跟虛擬
先說 適配器模式 (Adapter Pattern)
轉換一個不相容的介面,變成客戶端所期望的另一個介面。
它的主要目的是解決「不相容」的問題,讓原本無法一起工作的類別可以合作。
它扮演的是一個**「翻譯官」或「轉接頭」**的角色。
解決什麼問題?
當你想使用一個已經存在的類別,但它的介面(方法名稱、參數等)不符合你現在的需求。
當你想整合一個第三方的函式庫,但它的 API 設計與你的系統架構格格不入。
當你需要重構一個舊系統,讓新舊模組能夠並存溝通。
好的,我們用一個簡單且常見的 Java 範例來敘述「適配器模式」。
假設我們有一個新的音樂播放器系統,它定義了一個標準的播放介面 NewMediaPlayer
,所有新的音樂格式都會實現這個介面。
然而,我們還有一個舊的、無法修改的播放器 OldMediaPlayer
,它用來播放一種特殊的 .vlc
格式,而且它的方法名稱和我們的新介面完全不同。
我們的目標是:讓新的播放系統也能播放舊的 .vlc
格式,但又不能去修改舊播放器的程式碼。 這就是適配器模式大顯身手的時刻。
這是我們新的播放系統期望的標準介面。它只有一個簡單的 play
方法。
// Target Interface: 新的、標準的播放器介面
public interface NewMediaPlayer {
void play(String fileName);
}
這是舊的、介面不相容的播放器類別。注意它的方法名稱是 playVlcSong
,和我們的標準介面不同。
// Adaptee: 舊的、已存在的播放器,介面不相容
public class OldMediaPlayer {
public void playVlcSong(String fileName) {
System.out.println("使用舊版播放器播放 VLC 檔案: " + fileName);
}
}
這是整個模式的核心。我們建立一個 MediaPlayerAdapter
,它實現 (implements) 我們新的 NewMediaPlayer
介面,但在內部持有 (has-a) 一個 OldMediaPlayer
的實例。
當 play
方法被呼叫時,它會在內部將這個呼叫轉換成對 OldMediaPlayer
的 playVlcSong
方法的呼叫。
// Adapter: 適配器,讓舊播放器能適用於新介面
public class MediaPlayerAdapter implements NewMediaPlayer {
private OldMediaPlayer oldMediaPlayer;
// 在建構時,傳入需要被適配的舊物件
public MediaPlayerAdapter(OldMediaPlayer oldMediaPlayer) {
this.oldMediaPlayer = oldMediaPlayer;
}
@Override
public void play(String fileName) {
// 這裡就是「轉換」或「適配」的核心
// 客戶端呼叫 play(fileName),適配器內部將其轉為呼叫 oldMediaPlayer.playVlcSong(fileName)
System.out.println(">> 透過適配器...");
oldMediaPlayer.playVlcSong(fileName);
}
}
現在,我們的客戶端(例如主程式)可以無差別地使用 NewMediaPlayer
介面來播放所有音樂,完全不需要知道背後是否有一個舊的播放器在運作。
public class Main {
public static void main(String[] args) {
// 建立一個舊的播放器實例
OldMediaPlayer oldPlayer = new OldMediaPlayer();
// **關鍵步驟**: 使用適配器將舊播放器包裝起來,讓它看起來像一個新的播放器
NewMediaPlayer adaptedPlayer = new MediaPlayerAdapter(oldPlayer);
System.out.println("--- 播放 MP4 (新格式) ---");
// 假設我們有一個 Mp4Player 類別 (這裡省略實作)
// NewMediaPlayer mp4Player = new Mp4Player();
// mp4Player.play("music.mp4");
System.out.println("\n--- 播放 VLC (舊格式) ---");
// 對於客戶端來說,它根本不知道這是一個適配器,
// 它只知道這個物件符合 NewMediaPlayer 介面,可以直接呼叫 play() 方法。
adaptedPlayer.play("alone.vlc");
}
}
--- 播放 VLC (舊格式) ---
>> 透過適配器...
使用舊版播放器播放 VLC 檔案: alone.vlc
在這個範例中:
Client
(Main
類別) 只想使用 NewMediaPlayer
這個標準介面。OldMediaPlayer
介面不符,無法直接使用。MediaPlayerAdapter
就像一個轉接頭,它對外宣稱自己是一個 NewMediaPlayer
,但內部卻悄悄地將工作交給了 OldMediaPlayer
去完成。透過這種方式,我們在不修改任何既有程式碼的情況下,成功地讓兩個不相容的介面協同工作,完美體現了適配器模式的價值。