設計模式(十三)——代理模式(Proxy)

一、理解:

    代理模式,用於爲其他對象提供一種代理以控制對該對象的訪問。

    爲什麼不直接使用某個對象,卻要“畫蛇添足”的在外部增加一層,間接對其進行訪問?考慮下面的一些情況:

1、某個圖形界面初始化時,實例化某些對象需要較長時間。但我們又不希望界面停頓太久,因此,我們將這些對象的初始化時間進行控制,延遲到第一次使用的時候。

2、對於不同用戶,希望提供某個對象的不同方法,以控制該對象的訪問權限。

3、我們希望某些遠程方法的調用,就如在本地使用一樣方便。

以上這些情況下,我們就需要使用代理,用一個代理指向他,而僅提供代理給用戶使用。

類圖如下:


有以下的注意點:

1、    代理類Proxy和真正的類RealSubject都需要實現共同的接口(或者繼承某個類)Subject。

2、    在使用的時候,使用多態的性質,用Proxy替代RealSubject。

3、    Proxy類中,持有對於RealSubject對象的直接訪問。

這篇文章的例子還不錯:http://gongjiayun.iteye.com/blog/948778


下面是一些常用的代理:     

       (1) 遠程代理(Remote Proxy):爲一個位於不同的地址空間的對象提供一個本地的代理對象,這個不同的地址空間可以是在同一臺主機中,也可是在另一臺主機中,遠程代理又稱爲大使(Ambassador)。
       (2) 虛擬代理(Virtual Proxy):如果需要創建一個資源消耗較大的對象,先創建一個消耗相對較小的對象來表示,真實對象只在需要時纔會被真正創建。
       (3) 保護代理(Protect Proxy):控制對一個對象的訪問,可以給不同的用戶提供不同級別的使用權限。
       (4) 緩衝代理(Cache Proxy):爲某一個目標操作的結果提供臨時的存儲空間,以便多個客戶端可以共享這些結果。
       (5) 智能引用代理(Smart Reference Proxy):當一個對象被引用時,提供一些額外的操作,例如將對象被調用的次數記錄下來等。
       在這些常用的代理模式中,有些代理類的設計非常複雜,例如遠程代理類,它封裝了底層網絡通信和對遠程對象的調用,其實現較爲複雜。

二、Java中的動態代理

    代理模式,在代理類中封裝了一些方法,使得更方便的使用真是對象。在java中,提供了動態代理的方法。可以使用Proxy類的newProxyInstance靜態方法,實例化一個Proxy對象,提供給用戶使用。該方法有3個參數:(ClassLoader loader, Class<?>[] interfaces, InvocationHandlerh)

1、ClassLoader:被代理的類的類加載器。第二個參數中的所有的接口,都由該類加載器加載。

2、Class<?>[] interfaces,提供Proxy類需要實現的接口方法。

3、用戶要做的是寫一個實現了InvocationHandler接口的類,重寫invoke方法,該方法提供截取到方法調用時候的實現方案。

    可是,既然能夠通過實現一個類,該類持有真實對象的引用,來提供代理,爲什麼java還要費神提供給我們動態代理Proxy類呢?

1、有一個無法改變的類,他有幾十個方法,我只想重寫其中的某一個方法,並且提供原有類的所有方法。用上述的代理方式,需要定義原有的所有方法,調用realSubject.method(),代碼冗餘。

2、某個類,實現了多個接口,而我只想在代理類中,對某個接口的實現方式進行改變,而在上述的代理方式中,爲了接口的一致性,需要實現真實類的所有接口。

一個比較2的代碼案例如下:

	interface Run {
		void runit();
		void runit1();
	}
	
	interface Shout {
		void out();
	}

	class Car implements Run, Shout {
		@Override
		public void out() {
			System.out.println("car shout!");
		}

		@Override
		public void runit() {
			System.out.println("car run!");
		}

		@Override
		public void runit1() {
			System.out.println("car run1!");
		}

	}

	class ProxyInvocationHandler implements InvocationHandler {
		// 所有的方法調用,都會在這裏截獲。轉化爲該方法的調用
		@Override
		public Object invoke(Object obj, Method method, Object[] aobj)
				throws Throwable {
			System.out.println("proxy invoke!" + method.getName());
			return obj;
		}
	}
	
	public static void main(String[] args) {

		Car c = new Car();
		c.out();
		c.runit();

		Shout s = (Shout) Proxy.newProxyInstance(Shout.class.getClassLoader(),
				new Class<?>[] { Shout.class }, new ProxyInvocationHandler());
		Run r = (Run) Proxy.newProxyInstance(Run.class.getClassLoader(),
				new Class<?>[] { Run.class, Shout.class },
				new ProxyInvocationHandler());

		// 只能將代理對象轉爲爲相應的接口,如下代碼編譯出錯
		// Car cc = (Car) Proxy.newProxyInstance(Car.class.getClassLoader(),
		// new Class<?>[] { Car.class }, new ProxyInvocationHandler());
		s.out();
		r.runit();
		r.runit1();
	}


    Java中的動態代理,由於invoke捕獲的方法是在Proxy.newProxyInstance方法的第二個參數中,所指定的接口的方法。因此,可以理解爲返回一個類代理接口,該接口將該類實現的指定接口方法進行重寫。



 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章