關於代理模式和裝飾模式的一些思考

背景

筆者前幾天看了一個面試題 “說一下什麼是代理模式?”,於是回憶了一下這個設計模式,並結合一篇舊文 《理理 Java 開發中常見的設計模式》,溫故了一下這個知識點。

十年前看了好多遍的設計模式,死活處於學了忘、學不會的狀態;如今隨便翻到的某個 Java 技術點,都能快速反應出它們的前因後果。時光如梭,畢業已經十年了,這大概算是進入了賣油翁純熟的技藝階段了吧!

代理模式概述

代理模式類圖。面向對象編程語言中,代理對象和委託對象都需要實現相同接口,同時代理對象關聯一個真正的委託對象,客戶端得到的是一個代理的引用,實則背後調用的是真正委託對象的方法:
在這裏插入圖片描述
代理類型。 對於有大量類需要代理的應用,這就是一種負擔。一方面增加了工作量,而且還產生了大量相似的代理類,所以有了動態代理這個解決方案:不需要爲每個類都創建一個代理類,只在需要使用代理的時候,通過反射機制動態地生成一個實現代理接口的匿名類的實例。代理的分類:動態代理和靜態代理,即生成代理類的方式是什麼。

動態代理的兩種類型:JDKProxy 和 CGlib 兩種,二者比對結果如下

類型 原理 特點
JDKProxy 運行時直接寫Class字節碼 生成代理類效率高,反射執行效率低
CodeGeneratorLibrary ASM框架寫 Class 字節碼 生成代理類效率低,FastClass 機制直接調用方法,執行效率高

Java 而言,ASM 就是字節碼級別的編程,assembly ,即彙編語言。學過彙編的同學,有沒有感覺很親切呢?

JDKProxy。 JDK 是支持動態代理的,核心類爲 Proxy
在這裏插入圖片描述
JDK 的 Proxy 實現動態代理,優勢是,抽象統一的 InvocationHandler 實現類,完成公共的代理增強動作。然後參數是 targetInstance 。不需要定義 Proxy 實現類,直接通過動態代理生成代理類實例即可。

測試 JDKProxy 。根據這個類圖,來編寫一個動態代理的測試類如下。
第一步,定義代理接口類:

public interface Delegate {
	void log();
}

第二步,定義代理接口實現類:

public class DelegateImp implements Delegate{

	@Override
	public void log() {
		System.out.println("Class is "+this.getClass().getName());
	}
}

第三步,編寫測試類:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Date;

public class ProxyTest {
	public static void main(String[] args) {
		//這裏定義的是如何對委託類進行訪問
		DelegateImp imp = new DelegateImp();
		InvocationHandler handler = new InvocationHandler() {
			// proxy 這個類是最終的代理類
			// 自定義的 InvocationHandler 需要指定具體的 target 目標對象的
			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
				//調用委託類的方法之前的操作
				System.out.println("Start to call "+method.getName()+",time is:"+new Date());
				//調用委託類的方法
				method.invoke(imp, args);
				//調用委託類之後的操作
				System.out.println("Finish to call "+method.getName()+",time is:"+new Date());
				return null;
			}
			
		};
		
		//創建一個代理類
		Delegate proxy = (Delegate)Proxy.newProxyInstance(DelegateImp.class.getClassLoader(), DelegateImp.class.getInterfaces(), handler);
		proxy.log();
	}
}

運行結果:log 方法執行前後,多了一下增強動作。

Start to call log,time is:Mon Jan 20 10:59:54 CST 2020
Class is javastudy.DelegateImp
Finish to call log,time is:Mon Jan 20 10:59:54 CST 2020

裝飾模式概述

裝飾模式指的是在不必改變原類文件和使用繼承的情況下,動態地擴展一個對象的功能。它是通過創建一個包裝對象,也就是裝飾來包裹真實的對象。(來自百度百科的定義)

《Head first 設計模式》中描繪的該模式的靜態結構圖爲:
在這裏插入圖片描述

  1. 具體組件類和裝飾類必須實現相同的頂層接口 Component

  2. 具體裝飾實現類包含一個具體組件的引用,即裝飾類的構造過程需要傳入一個具體的組件類,以便對原組件類進行裝飾;

  3. 裝飾類可以對被裝飾者的行爲增強,在之前或之後加上自己的動作。

編程啓示錄

仔細分析了一下,發現代理模式和裝飾模式的區別有兩點:
第一點,關聯的類型不同。代理模式和裝飾模式的區別,從類圖結構上來看:

  • 代理關聯的是具體的委託對象;裝飾着裝飾的是裝飾者的接口類型;
  • 代理真正的委託對象,客戶端是不知道的;裝飾的具體對象,是由客戶端來指定的。

第二點,目的不同。代理,着重是控制對委託對象的訪問,由代理類本身決定誰是委託對象;裝飾,是對被裝飾對象的增強,由客戶端指定對誰進行裝飾。

另外,JDKProxy 動態代理的測試過程中,筆者想到的一些內容是:

JDKProxy 動態代理的核心是 InvokationHandler 的實現,它定義瞭如何對委託對象進行訪問。如果在實際開發中,增強操作可以複用的話,這個接口的實現類並不會很多。但是,如果每個委託對象,都有自己的行爲訪問控制方式的話,那就跟靜態代理沒有什麼區別了,因爲需要爲每個委託對象都創建一種 InvokationHandler 的實現類。
當然,就日常開發而言, InvokationHandler 基本上能夠複用,實現類的數量不會太多。

己亥年農曆臘月二十六,春節快到了,祝還在堅守崗位的朋友們,上班愉快……

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