Proxy模式(代理[延遲]模式)
Proxy??
Proxy是“代理人”的意思,它指的是代替別人進行工作的人。代理實際上就是使用委託的機制,在代理的過程中你可以做點其他的事情,然後再來執行被代理物件的程式碼。
- 知識儲備
1.什麼時候使用:
GoF書(請參見附錄E[GoF])在講解Proxy模式時,使用了一個可以在文字中嵌入圖形物件(例如圖片等)的文字編輯器作為例子。為了生成這些圖形物件,需要讀取圖片檔案,這很耗費時間。因此如果在開啟文件時就生成有所的圖形物件,就會導致文件開啟時間過長。所以,最好是當用戶瀏覽至文字中各個圖形物件時,再去生成它們的例項。這時,Proxy模式就有了用武之地。
2.有那些代理:
- Virtual Proxy(虛擬代理)Virtual Proxy就是本章中學習的Proxy模式。只有當真正需要例項時,它才生成和初始化例項。
- Remote Proxy(遠端代理)Remote Proxy可以讓我們完全不必在意RealSubject角色是否在遠端網路上,可以如同它在自己身邊一樣(透明性地)呼叫它的 方法。Java的RMI(RemoteMethodInvocation:遠端方法呼叫)就相當於Remote Proxy。
-
Access Proxy Access Proxy 用於在呼叫RealSubject角色的功能時設定訪問限制。例如,這種代理可以只允許指定的使用者呼叫方法,而當其他使用者呼叫方法時則報錯。
-
靜態代理
1.使用委託機制代理人只代理他能解決的問題。當遇到他不能解決的問題時,還是會“轉交”給本人去解決。
這裡的“轉交”就是在本書中多次提到過的“委託”。從PrinterProxy類的print方法中呼叫real.print方法正是這種“委託”的體現。
- 為了實現PrinterProxy類可以從printer類中分離出來作為獨立的元件使用,而且只要是實現了Printable介面的類都可以扮演Proxy的角色。需要使用反射例項
private synchronized void realize(String classname) { if(real==null){ try { real = ((Printable) Class.forName(classname).newInstance()); }catch (ClassNotFoundException e){ System.out.println("沒有發現"+classname); }catch (Exception e){ e.printStackTrace(); } } }
- 動態代理
在java的動態代理機制中,有兩個重要的類或介面,一個是 InvocationHandler(Interface)、另一個則是 Proxy(Class),這一個類和介面是實現我們動態代理所必須用到的。首先我們先來看看java的API幫助文件是怎麼樣對這兩個類進行描述的:
InvocationHandler:
InvocationHandler is the interface implemented by the invocation handler of a proxy instance. Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.
每一個動態代理類都必須要實現InvocationHandler這個介面,並且每個代理類的例項都關聯到了一個handler,當我們通過代理物件呼叫一個方法的時候,這個方法的呼叫就會被轉發為由InvocationHandler這個介面的 invoke 方法來進行呼叫。我們來看看
InvocationHandler這個介面的唯一一個方法 invoke 方法:
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
我們看到這個方法一共接受三個引數,那麼這三個引數分別代表什麼呢?
Object invoke(Object proxy, Method method, Object[] args) throws Throwable proxy: 指代我們所代理的那個真實物件 method: 指代的是我們所要呼叫真實物件的某個方法的Method物件 args: 指代的是呼叫真實物件某個方法時接受的引數
理清職責
-
實現一個帶名字的印表機
名字=====》》說明
Printer || 表示帶名字的印表機的類(本人)
Printable || Printer和PrinterProxy的共同介面
PrinterProxy || 表示帶名字的印表機的類(代理人)
Main ||測試程式行為的類
UML
類圖:

時序圖:

Code
- Printable
public interface Printable { // 設定列印名字 void setPrinterName(String name); // 獲取列印名字 String getPrinterName(); // 顯示文字 void print(String string); }
- Printer
···
public class Printer implements Printable{
private String name;
public Printer() { heavyjob("正在生成Printer例項"); } public Printer(String name) { this.name = name; heavyjob("正在生成Printer例項("+name+")"); } /** * 模擬一個高負載任務 * @param string */ private void heavyjob(String string) { System.out.println(string); for (int i = 0; i < 5; i++) { try { Thread.sleep(1000); }catch (InterruptedException e){ e.printStackTrace(); } System.out.print("."); } } @Override public void setPrinterName(String name) { this.name=name; } @Override public String getPrinterName() { return name; } @Override public void print(String string) { System.out.println("==="+name+"==="); System.out.println(string); }
}
···
- PrinterProxy
public class PrinterProxy implements Printable{ private String name; private Printer real; /** * 不論 setPrinterName 方法和getPrinterName 方法被呼叫多少次, * 都不會生成Printer類的例項。只有當真正需要本人時, * 才會生成printer類的例項(printerProxy類的呼叫者完全不知道是否生成了本人,也不用在意是否生成了本人)。 * @param name */ public PrinterProxy(String name) { this.name = name; } @Override public synchronized void setPrinterName(String name) { if(real!=null){ real.setPrinterName(name); } this.name=name; } @Override public String getPrinterName() { return name; } @Override public void print(String string) { realize(); real.print(string); } private synchronized void realize() { if(real==null) real=new Printer(name); } } public class MainT { public static void main(String[] args) { Printable p=new PrinterProxy("Tom"); System.out.println("現在是"+p.getPrinterName()); p.setPrinterName("Cat"); System.out.println("現在是"+p.getPrinterName()); p.print("我是 Tomcat"); } }
- 結果:
現在是Tom 現在是Cat 正在生成Printer例項(Cat) ..... ===Cat=== 我是 Tomcat
動態代理Code
public class Client { public static void main(String[] args) { Printable tom = new Printer("Tom"); DynamicProxy proxy = new DynamicProxy(tom); Printable o = (Printable) Proxy.newProxyInstance(proxy.getClass().getClassLoader(), tom.getClass().getInterfaces(), proxy); System.out.println(o.getClass().getName()); System.out.println("現在是"+o.getPrinterName()); o.print("Tomcat"); } } public class DynamicProxy implements InvocationHandler { private Object object; public DynamicProxy(Object object) { this.object = object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("準備執行"); System.out.println("Method:" + method); Object o = method.invoke(object, args); System.out.println("執行完畢"); return o; } } public interface Printable { // 設定列印名字 void setPrinterName(String name); // 獲取列印名字 String getPrinterName(); // 顯示文字 void print(String string); } public class Printer implements Printable{ private String name; public Printer() { heavyjob("正在生成Printer例項"); } public Printer(String name) { this.name = name; heavyjob("正在生成Printer例項("+name+")"); } /** * 模擬一個高負載任務 * @param string */ private void heavyjob(String string) { System.out.println(string); for (int i = 0; i < 5; i++) { try { Thread.sleep(1000); }catch (InterruptedException e){ e.printStackTrace(); } System.out.print("."); } } @Override public void setPrinterName(String name) { this.name=name; } @Override public String getPrinterName() { return name; } @Override public void print(String string) { System.out.println("==="+name+"==="); System.out.println(string); } }