阿里架構師淺析Java設計模式之虛擬代理模式
虛擬代理模式(Virtual Proxy)是一種節省記憶體的技術,它建議建立那些佔用大量記憶體或處理複雜的物件時,把建立這類物件推遲到使用它的時候。在特定的應用中,不同部分的功能由不同的物件組成,應用啟動的時候,不會立即使用所有的物件。在這種情況下,虛擬代理模式建議推遲物件的建立直到應用程式需要它為止。物件被應用第一次引用時建立並且同一個例項可以被重用。這種方法優缺點並存。
優點:
這種方法的優點是,在應用程式啟動時,由於不需要建立和裝載所有的物件,因此加速了應用程式的啟動。
缺點:
因為不能保證特定的應用程式物件被建立,在訪問這個物件的任何地方,都需要檢測確認它不是空(null)。也就是,這種檢測的時間消耗是最大的缺點。
應用虛擬代理模式,需要設計一個與真實物件具有相同介面的單獨物件(指虛擬代理)。不同的客戶物件可以在建立和使用真實物件地方用相應的虛擬物件來代替。虛擬物件把真實物件的引用作為它的例項變數維護。代理物件不要自動建立真實物件,當客戶需要真實物件的服務時,呼叫虛擬代理物件上的方法,並且檢測真實物件是否被建立。
如果真實物件已經建立,代理把呼叫轉發給真實物件,如果真實物件沒有被建立:
- 代理物件建立真實物件
- 代理物件把這個物件分配給引用變數。
- 代理把呼叫轉發給真實物件
按照這種安排,驗證物件存在和轉發方法呼叫這些細節對於客戶是不可見的。客戶物件就像和真實物件一樣與代理物件進行互動。因此客戶從檢測真實物件是否為null中解脫出來,另外,由於建立代理物件在時間和處理複雜度上要少於建立真實物件。因此,在應用程式啟動的時候,用代理物件代替真實物件初始化。
例子:
假設我們建立一個JAVA程式的整合開發環境(Integrated Development Environment),這個環境包括三個功能:編譯、執行、生成JavaDoc文件。在新建和編輯Java程式時,最為常用的是編譯和執行。至於生成JavaDoc文件對於每一個Java程式不是必需的。因此,在Java開發環境啟動時,不要建立和裝載實現整合開發環境全部功能的所有物件,僅建立那些在編輯、編譯、執行時用到的物件,保留提供生成JavaDoc文件的物件,這是一個好的設計思想。這種物件建立策略能夠高效地利用記憶體空間並且加快了整合開發環境的啟動速度。
假設編譯、執行、生成JavaDoc文件這些功能分別由三個工具類提供??Compiler、Runtime和JavaDoc。客戶物件可以訪問的不同IDE操作的介面以抽象類IDEOperation的形式定義。
public abstract class IDEOperation { private Compiler cmp; private Runtime rtime; public void compile(String javaFile) { cmp.compile(javaFile); } public void run(String classFile) { rtime.run (classFile); } //to be delayed until needed. public abstract void generateDocs(String javaFile); public IDEOperation() { cmp = new Compiler(); rtime = new Runtime(); } }
類IDEOperation提供了編譯、執行java程式方法的實現,作為它建構函式的一部分,IDEOperation建立和裝載了進行編譯和執行操作的Compiler和Runtime物件。生成JavaDoc文件的方法generateDocs方法被設計成抽象的方法,由它的子類來實現。
讓我們定義抽象類IDEOperation的一個具體子類RealProcessor。作為RealProcessor建構函式的一部分,建立JavaDoc物件來提供生成JavaDoc文件的服務,通過使用JavaDoc物件功能實現generateDocs方法。
public class RealProcessor extends IDEOperation { JavaDoc jdoc; public RealProcessor() { super(); jdoc = new JavaDoc(); } public void generateDocs(String javaFile) { jdoc.generateDocs(javaFile); } }
通過上面的實現,RealProcessor類包含了編譯、執行和生成JavaDoc文件的所有功能。像我們原來討論的,生成JavaDoc文件的功能不是每一個Java程式所必須的,當RealProcessor例項化的時候,包括負責生成JavaDoc文件的JavaDoc物件的一系列物件被建立。推遲建立JavaDoc物件有以下優點:
- 加速了RealProcessor物件的建立時間,因為它的建構函式建立的很少的物件。
- 高效地利用記憶體,因為在不需要物件服務的時候,不需要把物件保持在記憶體中。
在不改變RealProcessor實現的前提下,可以通過定義IDEOperation的另外一個子類ProxyProcessor來實現虛擬代理。因為RealProcessor和ProxyProcessor共享相同的介面,客戶物件可以用ProxyProcessor代替RealProcessor。圖25.1展示了類層次;
Figure 25.1: IDEOperation Class Hierarchy
public class ProxyProcessor extends IDEOperation { private RealProcessor realProcessor; public void generateDocs(String javaFile) { /* In order to generate javadocs the proxy loads the actual object and invokes its methods. */ if (realProcessor == null) { realProcessor = new RealProcessor(); } realProcessor.generateDocs(javaFile); } }
作為自己的例項變數,ProxyProcessor維護了RealProcessor物件的一個引用。作為generateDocs方法的一部分,ProxyProcessor檢測引用變數是否被初始化為RealProcessor物件。如果沒有被初始化,它建立一個RealProcessor物件並把這個物件分配給它的例項變數。一旦RealProcessor物件已經被建立,就呼叫其上的generateDocs方法。
實際上,也就是當客戶物件第一次請求產生javadoc文件時,RealProcessor才被初始化裝入記憶體中。反過來,直到客戶需要為Java程式生成javadocs時,JavaDoc物件才會被建立和裝入記憶體中。
客戶物件像呼叫真實處理物件一樣呼叫ProxyProcessor上的方法,並不需要關心(知道)RealProcessor物件是否存在。 至於驗證、檢測和ProxyProcessor和RealProcessor之間的互動、這樣的細節對於客戶物件是透明的。
public class Client { public static void main(String[] args) { /* At this point objects required for the compile and run operations are created, but not the objects that provide the generate Javadoc functionality. */ IDEOperation IDE = new ProxyProcessor(); IDE.compile("test.java"); IDE.run("test.class"); /* The Javadoc functionality is accessed For the first time and hence the Object offering the Javadoc generation Functionality is loaded at this point. */ IDE.generateDocs("test.java"); } }
【推薦閱讀】
Java程式設計師備戰“金九銀十”必備的面試技巧(附攜程Java崗面試