設計模式之代理模式

Hanyta發表於2024-05-13
  1. 為什麼要代理?

    • 解決在直接訪問物件時帶來的問題,比如說:要訪問的物件在遠端的機器上。在物件導向系統中,有些物件由於某些原因(比如物件建立開銷很大,或者某些操作需要安全控制,或者需要程序外的訪問),直接訪問會給使用者或者系統結構帶來很多麻煩,我們可以在訪問此物件時加上一個對此物件的訪問層。
  2. 什麼是代理模式?

    • 代理模式(Proxy Pattern)是一種結構性模式。代理模式為一個物件提供了一個替身,以控制對這個物件的訪問。即透過代理物件訪問目標目標物件,可以在目標物件實現的基礎上,增強額外的功能操作,即擴充套件目標物件的功能。
  3. 代理模式分為靜態代理和動態代理

    • 靜態:由程式設計師建立代理類或特定工具自動生成原始碼再對其編譯,在程式執行前代理類的 .class 檔案就已經存在了。
    • 動態:在程式執行時,運用反射機制動態建立而成。
  4. 靜態代理

    • 靜態代理是定義父類或者介面,然後被代理物件(即目標物件)與代理物件一起實現相同的介面或者是繼承相同父類。代理物件與目標物件實現相同的介面,然後透過呼叫相同的方法來呼叫目標物件的方法。

      • 優點:可不修改目標物件的功能,透過代理物件對目標功能擴充套件。
      • 缺點:因為代理物件需要與目標物件實現一樣的介面,所以會有很多代理類,一旦介面增加方法,目標物件與代理物件都要維護。
    • 舉例

    • ITeacherDao:介面

    • TeacherDao:目標物件,實現介面ITeacherDao

    • TeacherDAOProxy:代理物件,也實現ITeacherDao介面,並且聚合ITeacherDao屬性,透過構造器傳參設定值,呼叫的時候透過呼叫代理物件的方法來呼叫目標物件。

  5. 動態代理

    • 動態代理也叫JDK代理、介面代理。它使代理物件不需要實現介面(但目標物件要實現介面),代理物件的生成,是利用JDK的API,動態的在記憶體中構建代理物件。

    • 即使用JDK包java.lang.reflect.Proxy中的newProxyInstance方法來動態的建立目標物件(被代理物件),該方法需要如下接收三個引數:

      • ClassLoader loader:指定當前目標物件使用的類載入器
      • Class<?>[] interfaces :目標物件實現的介面型別,使用泛型方法確認型別
      • InvocationHandler h :事情處理,執行目標物件的方法時,會觸發事情處理器方法,把當前執行的目標物件方法作為引數傳入

    • 核心是getProxyInstacne()

      • 根據傳入的物件TeacherDao目標物件
      • 利用反射機制,返回一個代理物件
      • 然後透過代理物件,呼叫目標物件方法
    //介面
    public interface ITeacherDao {
    	void teach();
    	void test(String name);
    }
    //目標物件
    public class TeacherDao implements ITeacherDao {
    	@Override
    	public void teach() {
    		System.out.println("一鍵三連");
    	}
    	@Override
    	public void test(String name) {
    		System.out.println("傳參測試:" + name);
    	}
    }
    //代理物件
    public class ProxyFactory {
    	//維護一個目標物件 , Object
    	private Object target;
    	//構造器 , 對target 進行初始化
    	public ProxyFactory(Object target) {
    		this.target = target;
    	}
    	//動態生成一個代理物件
    	public Object getProxyInstance() {
    		return Proxy.newProxyInstance(target.getClass().getClassLoader(),
    				target.getClass().getInterfaces(),
    				new InvocationHandler() { //匿名類重寫invoke方法
    					@Override
    					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    						System.out.println("動態代理開始");
    						Object returnVal = method.invoke(target, args);//反射機制呼叫目標物件的方法
    						System.out.println("動態代理結束");
    						return returnVal;
    					}
    				});
    	}
    }
    //測試
    public class Client {
    	public static void main(String[] args) {
    		//建立目標物件
    		ITeacherDao target = new TeacherDao();
    
    		//建立代理物件
    		ITeacherDao proxyInstance = (ITeacherDao)new ProxyFactory(target).getProxyInstance();
    
    		//記憶體中動態生成了代理物件
    		System.out.println(proxyInstance.getClass());
    
    		//透過代理物件,呼叫目標物件的方法
    		proxyInstance.teach();
    		proxyInstance.test("一鍵三連");
    	}
    }
    

相關文章