Proxy 代理模式

動機

有時,我們需要對一個對象的訪問進行控制。比如說如果我們有一些開銷較大的對象,但是只需要使用其中一部分方法,我們只要在完全需要它們的時候才初始化這些對象。那時, 我們可以用一些暴露出同樣接口的輕量級對象來替代那些笨重的對象。這些輕量級的對象就稱為代理,并且它們只會在真正需要的時候才實例化那些笨重的對象,那時,我們就轉而使用一些輕量級的對象了。

這種對對象的訪問進行控制的需求源自于: 需要控制開銷較大的對象的實例化和初始化過程;賦予對象不同的訪問權限;以及提供多樣化的途徑來訪問和引用運行于其它進程或機器的對象;其它。

設想一個圖片預覽軟件。這種軟件必須能夠列舉和展示文件夾中的高清圖片。不過,人們有多頻繁打開文件夾并且查看里邊的所有圖片。有時,你要尋找特定的照片,有時,你只想要查看圖片名稱。圖片預覽程序必須能夠列出所有的照片對象,但是這些對象必須在需要渲染的時候才加載到內存中。

目的

此模式的目標是要為對象提供一個占位符來控制對其的引用

實現

proxy uml類圖
  • Subject 表示 RealSubject 要實現的接口。代理 Proxy 也要實現這個接口,這樣在使用 RealObject 的地方都能使用代理類 Proxy。
  • Proxy
    • 保持訪問 RealSubject 的引用
    • 實現 RealSubject 一樣的接口,這樣 Proxy 就能替代 RealSubject了
    • 控制 RealSubject 的訪問,可能還需要負責其它的創建和刪除
    • 根據代理的類型,其他職責
  • RealSubject 代理類代表的真正對象

說明

客戶端 client 持有一個 Proxy 的引用,然后 client 按照原先處理 RealSubject 一樣處理 Proxy 從而調用 doOperation() 。 這時,Proxy 在調用 RealSubject.doOperation() 之前可以做些其他的事情。 Proxy 可以新建一個 RealObject 對象,執行初始化操作,檢查 client 對該方法的調用許可,然后再調用方法。Proxy 也可以在調用 doOperation() 之后做一些額外的任務,如增加對象的引用計數。

適用范圍 & 例子

代理模式適用于需要控制對象的訪問的時候,也適用于對象的復雜引用。常見的情景如下:

  • Virtual Proxies - 虛擬代理 將開銷大的對象的創建和初始化延遲到需要的時候,對象根據需要來創建。
  • Remote Proxies - 遠程代理 為在不同地址空間的對象提供一個本地表示。常見的例子是 Java RMI 樁對象,這些樁對象在調用樁對象的方法時作為代理同處于其他機器上的遠程對象進行通信的方法調用。
  • Protection Proxies - 保護代理 代理對 RealSubject 的方法訪問進行控制,賦予部分對象訪問權限、拒絕其它對象的訪問。
  • Smart References - 智能引用 為指定的對象提供復雜的訪問控制。比如跟蹤一個對象的引用數量,如果超過特定的值就拒絕訪問。或者是按需從數據庫中將對象加載到內存里。

例子 - Virtual Proxy

如文章開頭講的,列出和展示高清圖片的圖片預覽程序需要顯示出所有照片的列表,不過在用戶從列表中選擇一張圖片之前不需要展示實體的圖片。


Virtual Proxy - Image Viewer Program

下面代碼中的 Image 接口代表前面提到的 Subject 。 這個接口中只有一個 showImage() 的方法,具體實現類必須實現它來把圖片渲染到屏幕。

package proxy;

/**
* Subject Interface
*/
public interface Image{
  public void showImage();
}

下面的代碼展示了代理的實現。圖片代理是個虛擬代理,它創建并按需加載真實的圖片對象,這樣可以節省將圖片加載進內存的開銷,直到渲染圖片的時候才需要加載。

package proxy;

public class ImageProxy implements Image{

    /**
     * 私有代理數據
     */
    private String imageFilePath;

    /**
     * 引用 RealSubject
     */
    private Image proxifiedImage;

    public ImageProxy(String imageFilePath){
        this.imageFilePath = imageFilePath;
    }
    
    @Override
    public void showImage() {

        // 只在需要渲染圖片的時候才創建圖片對象
        proxifiedImage = new HighResolutionImage(imageFilePath);
        
        proxifiedImage.showImage();
    }
}

以下是圖片預覽程序的代碼;程序中就簡單的加載了三張圖片,只渲染一張圖片,一次用代理模式 ImageProxy ,另一次直接使用真實對象 HighResolutionImage 。我們可以看到,當我們使用代理模式的時候,雖然三張圖片加載進來了,但是這高分辨率的圖片直到渲染的時候才加載。然而對于沒有使用代理模式的部分,盡管實際上只要渲染一張圖片,三張圖片也都加載到內存中了。

package proxy;

/**
 * Image Viewer Promgram
 */
public class ImageViewer {
    public static void main(String[] args) {
        // 假設用戶選中有三張圖片的文件夾

        // 創建3個圖片對象
        Image highResolutionImage1 = new ImageProxy("sample/veryHighResPhoto1.jpeg");
        Image highResolutionImage2 = new ImageProxy("sample/veryHighResPhoto2.jpeg");
        Image highResolutionImage3 = new ImageProxy("sample/veryHighResPhoto3.jpeg");

        // 假設用戶點擊列表中的一個圖片,這只會引起程序調用這個圖片的 showImage() 方法。
        // 注意,這種情況只會把這張圖片加載進內存
        highResolutionImage1.showImage();

        // 思考下, 如果直接使用 HighResolutionImage 對象會怎么樣
        Image highResolutionImageNoProxy1 = new HighResolutionImage("sample/veryHighResPhoto1.jpeg");
        Image highResolutionImageNoProxy2 = new HighResolutionImage("sample/veryHighResPhoto2.jpeg");
        Image highResolutionImageNoProxy3 = new HighResolutionImage("sample/veryHighResPhoto3.jpeg");

        // 如果用戶只選中了圖片2, 由于這種情況所有圖片都已經加載到內存中了,
        // 但是用戶并沒有查看所有圖片,內存就浪費了
        highResolutionImageNoProxy2.showImage();
        
    }

}

示例代碼: https://github.com/minorpoet/design-patterns/tree/master/proxy

具體問題和實現

Java 遠程方法調用(RMI)
在Java RMI 中在一個機器中(運行于JVM)的客戶端對象可以調用處于另一個機器上的遠程對象(另一個JVM)的方法。代理(也稱為樁)位于客戶端機器中,客戶端對象調用代理對象就好像遠程對象本身一樣(記住代理同樣實現了 RealSubject 實現的接口)。代理對象本身會同遠程對象進行通信,調用遠程對象的方法,有返回值的話就返回給客戶端。這種情形的代理就是 Remote Proxy 遠程代理。

相關模式

  • 適配器模式 適配器 adapter 實現了和其適配的對象 adaptee 不同的接口,而代理實現的接口和其主題對象一致
  • 裝飾器模式 裝飾器 decorator 的實現可以和代理一樣,不過裝飾器增強了的對象的功能,而代理控制了對象的訪問

總結

已知應用:

  • 如上述介紹 Java RMI 實現了遠程代理
  • 安全代理 Security Proxies 控制對象的訪問權限,可以在很多面向對象編程語言中發現其應用,包括 java, c#, c++...
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容