在HeadFirst設計模式中代理模式用了比較多的篇幅來講解,其中的例子我感覺有些繁瑣,所以我們這篇就不按照慣例用例子來闡述代理模式了。我們直接進入正題,分析模式本身的設計和解決的問題。
遠程代理模式
假如我們有一個系統,能夠調用本地對象,然后將每個請求轉發到遠程對象上進行調用應該如何設計。
在客戶端我們使用客戶輔助對象進行調用,客戶輔助對象進行遠端調用,對於客戶對象來說就像是在調用本地的方法一樣。
在服務端,服務輔助對象從客戶輔助對象中接受請求(socket連接),將調用的信息解包,然后調用真正服務對象上的方法。
我們利用代碼更清楚的看到實現過程和方式,書中利用java的RIM來進行遠程方法調用,我們不必糾結RIM,只要知道RIM是幫我們實現演出調用處理網絡和I/O代碼。
1、遠端接口
首先我們需要一個接口用於客戶輔助對象和服務輔助對象的統一接口。
public interface MyRemote extends Remote{ public String SayHello() throws RemoteException; }
Remote 是RIM包中的接口,使用RIM需要實現Remote接口。
2、遠端實現
服務實現遠端接口,也就是客戶端要調用的方法的接口。
public class MyRemoteImpl implements MyRemote{ public String SayHello(){ return "server say hello"; } }
3、注冊服務
現在我們已經實現了一個遠程服務了,要他能被客戶端遠程調用。就需要將服務實例化並注冊到RIM registry中,注冊使用了rmi 中的Naming類的靜態方法rebind()
我們可以直接在遠程服務的main() 方法中注冊就行了。
public static void main(String args[]){ try{ MyRemote service=new MyRemoteImpl(); Naming.rebind("RemoteHello",service); }catch(Exception ex){ ex.printStackTrace(); } }
4、客戶端實現
由於第三步我們已有了注冊服務的實現,客戶端要想調用遠端服務就需要通過網絡發現服務並調用。利用Naming.lookup()方法返回值並將他轉成遠端接口進行調用。
public class MyRemoteClient(){ public static void main(String[] args){ new MyRemoteClient().go(); } public void go(){ try{ MyRemote service=(MyRemote) Naming.lookup(rmi://127.0.0.1/RemoteHello); String result=service.SayHello(); System.out.println(result); }catch(Exception ex){ ex.printStackTrace(); } } }
整個執行過程:RIM啟動rmiregistry終端,啟動遠端服務運行到main()方法進行服務注冊。客戶端運行main()方法查找服務返回Object進行轉換到遠端接口對象,調用接口對象的方法進行代理訪問遠端服務。
在上面的代碼中部分代碼不完善只是講解遠程帶來和過程,同樣的.Net 實現遠程代理的一個經典用例就是WCF,看看WCF的模式是不是完美契合遠程代理模式。
代理模式
通過遠程代理模式我們已經知道代理模式的概念和一種實現了,遠程代理是一般代理模式的一種實現。因為代理模式包含許多變體,包括一般代理模式、虛擬代理模式、動態代理、緩存代理、同步代理等等。
這個類圖是一般代理模式的類圖。
首先Subject,它為RealSubject和Proxy提供了接口。通過實現同一接口,Proxy在RealSubject出現的地方取代它。
RealSubject是真正做事情的對象,它是被Proxy代理和控制訪問的對象。
Proxy持有RealSubject的引用。在某些時候,Proxy還會負責RealSubjext對象的創建與銷毀。
代理模式:為另一個對象提供一個替身或占位符以控制對這個對象的訪問。
使用代理模式創建代表對象,讓代表對象控制某對象的訪問,被代理的對象可以是遠程對象、創建開銷大的對象或者需要安全控制的對象。