設計模式之代理(Proxy)模式
在有些情況下,一個客戶不能或者不想直接訪問另一個對象,這時需要找一箇中介幫忙完成某項任務,這個中介就是代理對象。例如,購買火車票不一定要去火車站買,可以通過 12306 網站或者去火車票代售點買。又如找女朋友、找保姆、找工作等都可以通過找中介完成。
在軟件設計中,使用代理模式的例子也很多,例如,要訪問的遠程對象比較大(如視頻或大圖像等),其下載要花很多時間。還有因爲安全原因需要屏蔽客戶端直接訪問真實對象,如某單位的內部數據庫等。
定義與特點
由於某些原因需要給某對象提供一個代理以控制對該對象的訪問。這時,訪問對象不適合或者不能直接引用目標對象,代理對象作爲訪問對象和目標對象之間的中介。
代理模式的主要優點有:
- 代理模式在客戶端與目標對象之間起到一箇中介作用和保護目標對象的作用;
- 代理對象可以擴展目標對象的功能;
- 代理模式能將客戶端與目標對象分離,在一定程度上降低了系統的耦合度;
其主要缺點是:
- 在客戶端和目標對象之間增加一個代理對象,會造成請求處理速度變慢;
- 增加了系統的複雜度;
結構與實現
代理模式的結構比較簡單,主要是通過定義一個繼承抽象主題的代理來包含真實主題,從而實現對真實主題的訪問,下面來分析其基本結構和實現方法。
結構
代理模式的主要角色如下。
- 抽象主題(Subject)類:通過接口或抽象類聲明真實主題和代理對象實現的業務方法。
- 真實主題(Real Subject)類:實現了抽象主題中的具體業務,是代理對象所代表的真實對象,是最終要引用的對象。
- 代理(Proxy)類:提供了與真實主題相同的接口,其內部含有對真實主題的引用,它可以訪問、控制或擴展真實主題的功能。
其結構圖如圖 1 所示。
實現
代理模式的實現代碼如下:
package com.design.pattern.structuralPattern.proxyPattern;
public class ProxyTest {
public static void main(String[] args) {
Proxy proxy = new Proxy();
proxy.request();
}
}
//抽象主題
interface Subject{
void request();
}
class RealSubject implements Subject{
@Override
public void request() {
System.out.println("真實主題");
}
}
//proxy
class Proxy implements Subject{
private RealSubject realSubject;
@Override
public void request() {
preRequest();
if (realSubject == null){
realSubject = new RealSubject();
}
realSubject.request();
postRequest();
}
public void preRequest(){
System.out.println("訪問真實主題之前的準備...");
}
public void postRequest(){
System.out.println("訪問真實主題之後的處理...");
}
}
應用場景
前面分析了代理模式的結構與特點,現在來分析以下的應用場景。
- 遠程代理,這種方式通常是爲了隱藏目標對象存在於不同地址空間的事實,方便客戶端訪問。例如,用戶申請某些網盤空間時,會在用戶的文件系統中建立一個虛擬的硬盤,用戶訪問虛擬硬盤時實際訪問的是網盤空間。
- 虛擬代理,這種方式通常用於要創建的目標對象開銷很大時。例如,下載一幅很大的圖像需要很長時間,因某種計算比較複雜而短時間無法完成,這時可以先用小比例的虛擬代理替換真實的對象,消除用戶對服務器慢的感覺。
- 安全代理,這種方式通常用於控制不同種類客戶對真實對象的訪問權限。
- 智能指引,主要用於調用目標對象時,代理附加一些額外的處理功能。例如,增加計算真實對象的引用次數的功能,這樣當該對象沒有被引用時,就可以自動釋放它。
- 延遲加載,指爲了提高系統的性能,延遲對目標的加載。例如,
Hibernate
中就存在屬性的延遲加載和關聯表的延時加載。
模式的擴展
在前面介紹的代理模式中,代理類中包含了對真實主題的引用,這種方式存在兩個缺點。
- 真實主題與代理主題一一對應,增加真實主題也要增加代理。
- 設計代理以前真實主題必須事先存在,不太靈活。採用動態代理模式可以解決以上問題,如
Spring AOP
,其結構圖如圖 4 所示。