科技市場買配件找代理商:買方享受品保、優惠價、售後服務;廠商專注研發生產,銷售風險由代理分擔。美國甚至規定產品上市必經代理。軟體設計中,「代理」同樣扮演中介、控制存取、增值服務的關鍵角色。
代理模式:為其他物件提供一個代理以控制對該物件的存取。當客戶不想或無法直接引用目標物件時,代理物件介入,可過濾、記錄、延遲建立、權限控制等。
何時使用? 需對既有方法增強/修改,但不想違反「開閉原則」修改原始碼。引入「第三者」代理調用原方法並控制結果,職責劃分更清晰,利於維護。小系統或簡單邏輯則直接修改較划算。
| 類型 | 英文 | 核心目的 | 典型場景 |
|---|---|---|---|
| 遠程代理 | Remote Proxy | 為不同位址空間物件提供本地代表 | RMI、Web Service、微服務呼叫 |
| 虛擬代理 | Virtual Proxy | 延遲建立資源耗大/複雜物件 | 大圖片載入、資料庫連線池、報表產生器 |
| 保護代理 | Protection Proxy | 控制存取權限 | 論壇角色權限、API 閘道鑑權 |
| 智能引用代理 | Smart Reference Proxy | 提供額外服務(計數、記錄、快取) | 呼叫次數統計、記憶體快取、日誌記錄 |
| 複製代理 | Copy-on-Write Proxy | 寫入時才複製物件 | 大型資料結構共享、快照隔離 |
| 防火牆代理 | Firewall Proxy | 保護目標免受惡意存取 | 網路防火牆、沙箱隔離 |
| 同步代理 | Synchronization Proxy | 多執行緒安全存取 | 線程安全集合、分散式鎖 |
| 快取代理 | Cache Proxy | 暫存運算結果 | 網頁快取、查詢結果快取、CDN |

// 抽象主題
interface Image {
void display();
}
// 真實主題:耗資源載入
class RealImage implements Image {
private final String filename;
public RealImage(String filename) {
this.filename = filename;
loadFromDisk(); // 模擬耗時操作
}
private void loadFromDisk() { System.out.println("Loading " + filename); }
public void display() { System.out.println("Displaying " + filename); }
}
// 代理主題:延遲建立
class ProxyImage implements Image {
private RealImage realImage;
private final String filename;
public ProxyImage(String filename) { this.filename = filename; }
public void display() {
if (realImage == null) realImage = new RealImage(filename);
realImage.display();
}
}
// 客戶端
public class ProxyDemo {
public static void main(String[] args) {
Image img = new ProxyImage("huge_photo.tiff");
System.out.println("Image object created, but not loaded yet.");
img.display(); // 首次顯示觸發載入
img.display(); // 二次顯示直接使用已建立物件
}
}
Java 反射 API (java.lang.reflect.Proxy) 支援介面型動態代理;CGLIB 庫透過繼承實現類別型動態代理。Spring AOP、Hibernate 惰性載入、RPC 框架廣泛應用動態代理攔截方法調用,實現事務、快取、安全、監控等橫切關注點。
代理模式以「間接層」解耦存取控制與核心邏輯,從靜態代理到動態代理,從單一物件到分散式系統,無處不在。掌握八大類型與適用場景,能讓架構更彈性、安全、可維護。