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);
}
}