【設計模式】-代理模式

模式動機

在某些狀況下,一個客戶不想或者不能直接引用一個對 象,此時能夠經過一個稱之爲「代理」的第三者來實現 間接引用。代理對象能夠在客戶端和目標對象之間起到 中介的做用,而且能夠經過代理對象去掉客戶不能看到 的內容和服務或者添加客戶須要的額外服務。javascript

經過引入一個新的對象(如小圖片和遠程代理 對象)來實現對真實對象的操做或者將新的對 象做爲真實對象的一個替身,這種實現機制即 爲代理模式,經過引入代理對象來間接訪問一 個對象,這就是代理模式的模式動機。html

模式定義

代理模式(Proxy Pattern) :給某一個對象提供一個代 理,並由代理對象控制對原對象的引用。代理模式的英 文叫作Proxy或Surrogate,它是一種對象結構型模式。java

分類:

1.靜態代理

2.動態代理

靜態代理:

所謂靜態代理,就是在編譯階段就生成代理類來完成對代理對象的一系列操做。下面是代理模式的結構類圖:

代理模式的角色分四種:

主題接口: 即代理類的所實現的行爲接口。
目標對象: 也就是被代理的對象。
代理對象: 用來封裝真是主題類的代理類
客戶端git

下面是代理模式的類圖結構:

這裏寫圖片描述

代理模式的實現思路

代理對象和目標對象均實現同一個行爲接口。
代理類和目標類分別具體實現接口邏輯。
在代理類的構造函數中實例化一個目標對象。
在代理類中調用目標對象的行爲接口。
客戶端想要調用目標對象的行爲接口,只能經過代理類來操做。github

實例:

靜態代理的實例

下面以一個延遲加載的例子來講明一下靜態代理。咱們在啓動某個服務系統時,加載某一個類時可能會耗費很長時間。爲了獲取更好的性能,在啓動系統的時候,咱們每每不去初始化這個複雜的類,取而代之的是去初始化其代理類。這樣將耗費資源多的方法使用代理進行分離,能夠加快系統的啓動速度,減小用戶等待的時間。面試

定義一個主題接口

public interface Subject {
    public void sayHello();
    public void sayGoodBye();
}

定義一個目標類, 並實現主題接口

public class RealSubject implements Subject {
    public void sayHello() {
        System.out.println("Hello World");
    }

    public void sayGoodBye() {
        System.out.println("GoodBye World");
    }
}

定義一個代理類,來代理目標對象

public class StaticProxy implements Subject {

    Private RealSubject realSubject = null;

    public StaticProxy() {}

    public void sayHello() {
        //用到時候才加載,懶加載
        if(realSubject == null) {
            realSubject = new RealSubject();
        }
        realSubject.sayHello();
    }

    //sayGoodbye方法同理
    ...
}

定義一個客戶端

public class Client {
    public static void main(String [] args) {
        StaticProxy sp = new StaticProxy();
        sp.sayHello();
        sp.sayGoodBye();
    }
}

以上就是靜態代理的一個簡單測試例子。感受可能沒有實際用途。然而並不是如此。使用代理咱們還能夠將目標對象的方法進行改造,好比數據庫鏈接池中建立了一系列鏈接,爲了保證不頻繁的打開鏈接,這些鏈接是幾乎不會關閉的。然而咱們編程總有習慣去將打開的 Connection 去 close 。 這樣咱們就能夠利用代理模式來從新代理 Connection 接口中的 close 方法,改變爲回收到數據庫鏈接池中而不是真正的執行 Connection.close 方法。其餘的例子還有不少,具體須要本身體會數據庫

動態代理:

動態代理是指在運行時動態生成代理類。即,代理類的字節碼將在運行時生成並載入當前代理的 ClassLoader。與靜態處理類相比,動態類有諸多好處。編程

優勢:

不須要爲真實主題寫一個形式上徹底同樣的封裝類,假如主題接口中的方法不少,爲每個接口寫一個代理方法也很麻煩。若是接口有變更,則真實主題和代理類都要修改,不利於系統維護;
使用一些動態代理的生成方法甚至能夠在運行時制定代理類的執行邏輯,從而大大提高系統的靈活性。設計模式

方式:

生成動態代理的方法有不少: JDK中自帶動態代理,CGlib, javassist等。這些方法各有優缺點。本文主要探究JDK中的動態代理的使用和源碼分析。微信

動態代理實例:

下面用一個實例講解一下JDK中動態代理的用法:

public class dynamicProxy implements InvocationHandler {

    private RealSubject = null;

    public Object invoke(Object proxy, Method method, Object[] args){
        if(RealSubject == null) {
            RealSubject = new RealSubject();
        }
        method.invoke(RealSubject, args);
        return RealSubject;
    }

}

客戶端代碼實例

public class Client {
    public static void main(Strings[] args) {
        Subject subject = (Subject)Proxy.newInstance(ClassLoader.getSystemLoader(), RealSubject.class.getInterfaces(), new DynamicProxy());
        Subject.sayHello();
        Subject.sayGoodBye();
    }
}

從上面的代碼能夠看出,要利用JDK中的動態代理。利用靜態方法Proxy.newInstance(ClassLoader, Interfaces[], InvokeHandler)能夠建立一個動態代理類。 newInstance方法有三個參數,分別表示類加載器,一個但願該代理類實現的接口列表,以及實現InvokeHandler接口的實例。 動態代理將每一個方法的執行過程則交給了Invoke方法處理。

優勢

代理模式可以協調調用者和被調用者,在必定程度上下降了系 統的耦合度。

遠程代理使得客戶端能夠訪問在遠程機器上的對象,遠程機器 可能具備更好的計算性能與處理速度,能夠快速響應並處理客戶端請求。

虛擬代理經過使用一個小對象來表明一個大對象,能夠減小系 統資源的消耗,對系統進行優化並提升運行速度。

保護代理能夠控制對真實對象的使用權限。

缺點:

因爲在客戶端和真實主題之間增長了代理對象,所以 有些類型的代理模式可能會形成請求的處理速度變慢。

實現代理模式須要額外的工做,有些代理模式的實現 很是複雜。

適用環境:

遠程(Remote)代理:爲一個位於不一樣的地址空間的對象提供一個本地 的代理對象,這個不一樣的地址空間能夠是在同一臺主機中,也但是在 另外一臺主機中,遠程代理又叫作大使(Ambassador)。

虛擬(Virtual)代理:若是須要建立一個資源消耗較大的對象,先建立一個消耗相對較小的對象來表示,真實對象只在須要時纔會被真正建立。

Copy-on-Write代理:它是虛擬代理的一種,把複製(克隆)操做延遲 到只有在客戶端真正須要時才執行。通常來講,對象的深克隆是一個 開銷較大的操做,Copy-on-Write代理可讓這個操做延遲,只有對象被用到的時候才被克隆。

保護(Protect or Access)代理:控制對一個對象的訪問,能夠給不一樣的用戶提供不一樣級別的使用權限。

緩衝(Cache)代理:爲某一個目標操做的結果提供臨時的存儲空間,以便多個客戶端能夠共享這些結果。

防火牆(Firewall)代理:保護目標不讓惡意用戶接近。

同步化(Synchronization)代理:使幾個用戶可以同時使用一個對象而沒有衝突。

智能引用(Smart Reference)代理:當一個對象被引用時,提供一些額外的操做,如將此對象被調用的次數記錄下來等。

個人微信二維碼以下,歡迎交流討論

這裏寫圖片描述

歡迎關注《IT面試題彙總》微信訂閱號。天天推送經典面試題和麪試心得技巧,都是乾貨!

微信訂閱號二維碼以下:

這裏寫圖片描述

參考:
head first 設計模式
http://design-patterns.readthedocs.io/zh_CN/latest/structural_patterns/proxy.html
https://github.com/biezhi/java-bible/blob/master/designpatterns/proxy.md

<script type="text/javascript"> $(function () { $('pre.prettyprint code').each(function () { var lines = $(this).text().split('\n').length; var $numbering = $('<ul/>').addClass('pre-numbering').hide(); $(this).addClass('has-numbering').parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($('<li/>').text(i)); }; $numbering.fadeIn(1700); }); }); </script>
相關文章
相關標籤/搜索