模式、延遲載入、動態等 4 個角色。
該模式使用物件來完成使用者請求,並阻止使用者訪問真實物件。 現實世界中的**人被授權執行當事人的某些事務,而不需要當事人出面,從第三方的角度來看,當事人似乎不存在,因為他只與**人溝通。 事實上,第一人必須得到當事人的授權,核心問題需要徵求當事人的意見。
在軟體設計中,使用**模式的意圖有很多,比如出於安全原因需要阻止客戶端直接訪問真實物件,或者需要使用**類來處理遠端呼叫中遠端方法呼叫(如rmi)的技術細節,或者為了提高系統效能而封裝真實物件, 從而達到延遲載入的目的。
*有 4 種型別的模式角色:
Topic Interface:定義類和真實主題的常用外部方法,也是類真實主題的方法;真實主題:實際實現業務邏輯的類;類:用於封裝真實主題main:客戶端,它使用類和主題介面來做一些工作。 乙個簡單的例子來說明如何使用 ** 模式實現延遲載入及其含義。 假設客戶端軟體能夠根據使用者請求查詢資料庫中的資料。 在查詢資料之前,需要獲取資料庫連線,開啟軟體後,初始化系統的所有類,然後嘗試獲取資料庫連線。 當系統上有大量類似的操作(例如.XML解析等)時,所有這些初始化操作的組合會使系統的啟動速度非常慢。 為此,資料庫查詢中的初始化操作封裝在 Schema 類中,該類在系統啟動時初始化,而不是實際的資料庫查詢類,後者不執行任何操作。 因此,它的建設速度相當快。
當系統啟動時,使用**模式將最耗費資源的方法分開,這樣可以加快系統的啟動速度,減少使用者的等待時間。 當使用者實際進行查詢操作時,**類會單獨載入真實的資料庫查詢類,完成使用者的請求。 這個過程是用**模式來實現延遲載入。
延遲載入的核心思想是,如果你目前沒有使用這個元件,你不需要實際初始化它,用物件替換它,只在你真正需要的時候載入它。 **模式的延遲載入很有意義,首先,它可以在時間軸上分散對系統的壓力,尤其是在系統啟動時,而不必完成所有的初始化工作,從而加快了啟動時間;其次,對於許多現實世界的主體來說,從軟體啟動到關閉,可能根本就沒有呼叫過它,初始化這些資料無疑是對資源的浪費。 例如,這是使用 ** 類封裝資料庫查詢類後系統啟動過程的示例。 如果系統不使用 ** 模式,則需要在啟動時初始化 dbquery 物件,而使用 ** 模式時,只需在啟動時初始化乙個輕量級物件 dbqueryproxy。
下面的 idbquery 是乙個主題介面,它定義了類和真實類需要提供給外界的服務,並定義了公共方法 request() 函式來實現資料庫查詢。 dbquery 是真正的主題,負責實際的業務操作,dbqueryproxy 是 dbquery 的 ** 類。
清單 1延遲載入**。
公共介面 IDBQuery 公共類 dbQuery 實現 IDBQueryCatch(InterruptedException EX)}@overridepublic 字串 request() 公共類 dbQueryProxy 實現 idbQuery 在多執行緒環境中,返回乙個假類,類似於 future 模式返回 realrequest();public class main }
動態是指在執行時動態生成類。 也就是說,類的位元組碼將在執行時生成並載入到當前類載入器中。 與靜態處理類相比,動態類具有許多優點。 首先,你不需要為真正的主題寫乙個形式上一模一樣的包裝類,如果主題介面中有很多方法,為每個介面寫乙個**方法也很麻煩。 如果介面發生變化,必須修改真正的主題和**類,不利於系統維護其次,使用一些動態生成方法,甚至可以在執行時制定類的執行邏輯,大大提高了系統的靈活性。
Dynamic 類使用位元組碼動態生成載入技術在執行時生成載入類。 生成動態類的方法有很多種,例如 JDK 的本機動態處理、CGLIB、J**assist 或 ASM 庫。 JDK 的動態簡單易用,並且內置於 JDK 中,因此無需引入第三方 JAR 包,但它相對較弱。 CGLIB 和 J**Assist 是高階位元組碼生成庫,通常比 JDK 附帶的動態庫更好,並且非常強大。 ASM 是乙個底層的位元組碼生成工具,使用 ASM 幾乎就像用 j**a 位元組碼程式設計一樣,對開發者的要求最高,當然也是效能最高的動態生成工具。 但是,ASM使用起來很麻煩,效能也提公升不了幾個數量級,與CGLIB等高階位元組碼生成工具相比,ASM程式的可維護性較差,如果不是在效能要求苛刻的情況下,或者推薦使用CGLIB或J**Assist。
以清單 1 中的 dbqueryproxy 為例,使用 dynamic 生成乙個動態類,而不是上面示例中的 dbqueryproxy。 首先,使用 JDK 的動態物件生成。 JDK 的動態需要實現乙個處理方法呼叫的處理程式,該處理程式用於實現方法的內部邏輯。
清單 2動態**。
import j**a.lang.reflect.invocationhandler;import j**a.lang.reflect.method;公共類 DbQueryHandler 實現 InvocationHandler 以返回 realqueryrequest();
如您所見,其內部邏輯類似於 dbqueryproxy。 在呼叫真實主題的方法之前,請嘗試生成乙個真實主題物件。 接下來,您需要使用此處理程式生成動態物件。 **如清單 3 所示。
清單 3生成動態物件。
import j**a.lang.reflect.invocationhandler;import j**a.lang.reflect.method;import j**a.lang.reflect.proxy;公共類 DbQueryHandler 實現 InvocationHandler 以返回 realqueryrequest();public static idbquery createproxy(),new dbqueryhandler())return proxy;}}
上面生成了乙個實現 idbquery 介面的類,該類的內部邏輯由 dbqueryhandler 決定。 生成類時,newproxyinstance() 方法返回該類的例項。 至此,乙個完整的動態**就完成了。
在 J.A. 中,動態類的生成主要涉及類載入器的使用。 以 CGLIB 為例,要使用 CGLIB 生成動態,首先需要生成乙個 Enhancer 類的例項,並指定處理業務的類。 在增強劑中create() 方法,DefaultGeneratorStrategygenerate() 方法為動態類生成位元組碼並將其儲存在位元組陣列中。 然後使用 reflectutilsdefineClass() 方法,通過反射呼叫類載入器defineClass() 方法將位元組碼載入到類載入器中,以完成類的載入。 最後,使用 reflectutilsnewinstance() 方法通過反射生成乙個動態類的例項並返回該例項。 基本過程是基於指定的 ** 類生成類位元組碼(通過 defineclass() 將位元組碼定義為類),並使用反射機制生成該類的例項。 從清單 4 到清單 7 顯示了使用 CGLIB Dynamic Reflection 生成類的完整過程。
清單 4定義介面。
public interface bookproxy
清單 5定義實現類。
這個類沒有宣告 bookproxy 介面,public class, bookproxyimpl }
清單 6定義反射類和過載方法。
import j**a.lang.reflect.method;import net.sf.cglib.proxy.enhancer;import net.sf.cglib.proxy.methodinterceptor;import net.sf.cglib.proxy.methodproxy;公共類 bookproxylib 實現 MethodInterceptor @override **MethodPublic 物件 intercept(object obj, method method, object args,methodproxy proxy) throws throws throwable }
清單 7執行程式。
public class testcglib }
清單 8執行輸出。
事情開始增加普通的書籍方式。 事情結束了
**模式用於各種應用,如下所述:
遠端,即在不同的位址空間中提供物件的本地表示,這隱藏了物件存在於不同位址空間的事實。 比如webservice,當我們在應用的專案上新增乙個web引用,引用乙個webservice時,這時會在專案中宣告乙個webreference資料夾和一些檔案,這很有用,這樣客戶端程式就可以呼叫**來解決遠端訪問的問題;虛擬是根據需要建立昂貴的物件,通過這些物件需要很長時間才能例項化的真實物件。 這樣一來,就可以實現效能優化,比如開啟乙個網頁,裡面有大量的文字,但是我們很快就能看到文字,但是只能一一看到,那些未開啟的盒子,通過虛擬生成,被真實的**替換,這時**的路徑和大小被真實**儲存起來;Security**,用於控制訪問真實物件時的許可權。 當物件應具有不同的訪問許可權時,通常使用它指標引用是指呼叫真實物件,但執行其他操作時。 例如,統計對真實物件的引用次數,以便在物件沒有引用時自動釋放,或者在第一次引用永續性物件時將其載入到記憶體中,或者在訪問真實物件之前檢查它是否已釋放,以確保其他物件無法更改它。 這些都是通過在訪問物件時附加一些內務來完成的延遲載入,乙個在**模式下實現延遲載入的經典應用程式,在Hibernate框架中。 當 Hibernate 載入實體 Bean 時,它不會一次載入資料庫中的所有資料。 預設情況下,它採用延遲載入機制來提高系統效能。 Hibernate 中的延遲載入分為兩種型別:屬性的延遲載入和相關表的延遲載入。 實現原理是用**擷取原有的getter方法,在物件資料真正使用的時候才去資料庫或者其他第三方元件載入實際資料,從而提高系統效能。 設計模式是對前人工作的總結和提煉。 通常,廣為流傳的設計模式是針對特定問題的成熟解決方案。 如果明智地使用設計模式,系統不僅可以更容易被其他人理解,而且可以使系統結構更加合理。 本文對模式的4個作用、延遲載入、動力學等做了一些介紹,希望能幫助讀者對模式有進一步的了解。