設計模式——借助代理模式體驗"間接的美"(一)

引言

在古代《三國志·諸葛亮傳》中有這么一句話——“政事無巨細咸于亮。”;在唐代張九齡的《謝賜大麥面狀》中還有這么一句——“勤儉于生人,事必躬親,動合天德兩個典故連起來就是“事無巨細,事必躬親”,意思是無論大事小事都要親自過問,親力親為,雖然這樣的精神及其可貴。但在我們當代的生活中如果我們皆事必躬親的話,那么我們的生活將會是十分疲倦的,畢竟任何人的精力都是有限的,每一個人或事都有自己擅長的事或工作,同樣地我們的程序也是如此,每一個業務類都應該只專注于核心功能,而把其他繁雜的功能外包出去給代理類,比如說我們找房子,我們核心主題就是找到房子租下,如果我們自己找的話,得一家家聯系,商討最后決定價格、簽約付款,而采用代理模式的話就讓代理者去完成這些繁瑣的細節,作為客戶端我們甚至不需要知道中間的細節。

一、代理模式概述

代理模式(Proxy Pattern)又叫委托模式屬于是一個使用率非常高結構型設計模式。其定義如下:為其他對象提供一種代理以控制對這個對象的訪問。(Provide a surrogate or placeholder for another object to control access to it.)通俗可以理解成代理代理,就是幫你打理。你只跟代理一個人打交到。而不關心實際操作的人的具體如何做。代理模式目前框架里用的最多,主要作用是程序本身不關心被代理的身份細節。而只關心它暴露出來的共有行為接口,這個代理之所以理解困難,是有時候它像個工廠模式,有時候它像適配模式。也可從字面理解就是代理你去執行調用別的類的方法面向被調用的類。所以代理模式一般都會涉及到四個角色:抽象主題繼承或實現抽象主題的真實主題代理類、客戶類。

這里寫圖片描述

  • 抽象主題——負責聲明真實主題和代理的共同接口方法,同樣的可以定義為接口或抽象類

  • 繼承或實現抽象主題的真實主題——定義了代理所表示的真實對象,由其執行具體的業務邏輯方法,也就是說當客戶類通過代理類去執行操作時,本質就是調用真實主題的方法,所以這個類又被稱為被委托類被代理類

  • 代理類——該類會持有真實主題類的引用,并在其所實現的接口方法中完成對真實主題類的相關方法的調用(即為其他對象提供一種代理以控制對這個對象的訪問),所以這個類又被稱為委托類代理類

  • 客戶類——你前面這個類構建出了代理模式,總得有人使用吧,這個所謂的客戶類,簡單來說就是你使用代理模式的那個類。

二、代理模式的優點和缺點及可用場景

1、代理模式的共同優點

  • 能夠協調調用者和被調用者,在一定程度上降低了系統的耦合度。

  • 客戶端可以針對抽象主題角色進行編程,增加和更換代理類無須修改源代碼,符合開閉原則,系統具有較好的靈活性和可擴展性。

  • 遠程代理為位于兩個不同地址空間對象的訪問提供了一種實現機制,可以將一些消耗資源較多的對象和操作移至性能更好的計算機上,提高系統的整體運行效率。

  • 虛擬代理通過一個消耗資源較少的對象來代表一個消耗資源較多的對象,可以在一定程度上節省系統的運行開銷。

  • 緩沖代理為某一個操作的結果提供臨時的緩存存儲空間,以便在后續使用中能夠共享這些結果,優化系統性能,縮短執行時間。

  • 保護代理可以控制對一個對象的訪問權限,為不同用戶提供不同級別的使用權限。

2、代理模式的缺點

  • 由于在客戶端和真實主題之間增加了代理對象,因此有些類型的代理模式可能會造成請求的處理速度變慢,例如保護代理。
  • 實現代理模式需要額外的工作,而且有些代理模式的實現過程較為復雜,例如遠程代理。

3、適用場景

無法或者不想直接訪問某個對象訪問直接某個對象消耗巨大時,可以采取通過一個代理對象來間接訪問,為了保持對客戶端透明,代理對象和被代理對象需要實現相同的接口

  • 當客戶端對象需要訪問遠程主機中的對象時可以使用遠程代理。

  • 當需要用一個消耗資源較少的對象來代表一個消耗資源較多的對象,從而降低系統開銷、縮短運行時間時可以使用虛擬代理,例如一個對象需要很長時間才能完成加載時。

  • 當需要為某一個被頻繁訪問的操作結果提供一個臨時存儲空間,以供多個客戶端共享訪問這些結果時可以使用緩沖代理。通過使用緩沖代理,系統無須在客戶端每一次訪問時都重新執行操作,只需直接從臨時緩沖區獲取操作結果即可。

  • 當需要控制對一個對象的訪問,為不同用戶提供不同級別的訪問權限時可以使用保護代理。

  • 當需要為一個對象的訪問(引用)提供一些額外的操作時可以使用智能引用代理。

三、代理模式的實現形式

通常按照代碼機制可以分為靜態代理和動態代理兩大類,而按照使用范圍可以分為以下常用的四種:遠程代理(Remote Proxy)虛擬代理(Virtual Proxy)保護代理(Protection Proxy)智能引用(Smart Reference,又叫計數代理),值得注意的是兩大分類機制并不獨立,就是說無論是靜態代理還是動態代理都可以采用以上四種形式,實現起來也很簡單,無論是什么形式步驟和基本思想都大同小異,以法律代理簡化流程為例。

1、靜態代理

1.1、根據具體業務定義自己的抽象主題

可以定義成抽象類也可以定義成接口。

package proxy;

public interface ILegalSuit {
    void submit();//提交訴訟申請
    void proof();//進行舉證
    void debate();//法庭辯護
}

1.2、繼承或實現自抽象主題的真實主題

真實主題其實就是被代理對象。

/**
 * 
 * @author cmo
 *訴訟人,本質上這些訴訟流程的具體事宜都是由訴訟人去定義實現的
 */
public class LegalSuiter implements ILegalSuit {

    public void submit() {
        System.out.println("起草訴訟書并提交至法院");  
    }

    public void proof() {
        System.out.println("采集證據并向法院舉證");   
    }

    public void debate() {
        System.out.println("庭內辯護"); 
    }

}

1.3、繼承或實現自抽象主題的代理對象

實現代理對象主要步驟可以分為四步,第一,繼承或實現自抽象主題,第二,持有被代理對象的引用并通過構造方法傳入,第三,通過引用完成對被代理對象方法的調用,四,根據業務新增自己的邏輯。

package proxy;

/**
 * 
 * @author cmo
 *訴訟代理類,一般代理類的方法實現很簡單就是持有被代理對象,
 *并且實現抽象主題的方法里使用被代理對象的引用調用被代理類的方法,
 *當然代理類中還可以定義其他的方法邏輯,也可以在調用被代理對像的方法前后增刪邏輯
 */
public class LegalSuiterProxyer implements ILegalSuit {
    private ILegalSuit suiter;
    //通過構造方法把被代理對象傳遞過來
    public LegalSuiterProxyer(ILegalSuit suiter){
        this.suiter=suiter;
    }

    @Override
    public void submit() {
        suiter.submit();
    }

    @Override
    public void proof() {
        suiter.proof();
    }

    @Override
    public void debate() {
        suiter.debate();
    }
}

測試

public class Client {
    /**
     * @param args
     */
    public static void main(String[] args) {
        ILegalSuit suiter=new LegalSuiter();//首先構造一個被代理對象
        LegalSuiterProxyer proxyer=new LegalSuiterProxyer(suiter);//創建一個與被代理對象綁定的代理對象
        proxyer.submit();
        proxyer.proof();
        proxyer.debate();
    }
}

前面說過代理模式又叫做委托模式,因為代理模式本質上就是一個委托機制被代理對象方法的執行委托給代理對象,客戶端想要去完成相關操作的時候不需要直接去調用被代理對象,只需要簡單地去找代理對象即可,而且擴展性也很高,比如說某天前代理人的訴訟已經完成了,那么代理人不可能也不工作了吧,他還想幫別人代理訴訟的話,采用代理模式就顯示出很大的優勢,要做的只是再新增一個被代理對象并傳入到代理對象中即可,完全不會影響到前代理人的邏輯,高度解耦。那么何謂靜態代理呢,從上例可以得出,代理對象被代理對象并不是1:1的關系,代理對象可以代理多個相似的被代理對象,他們之中有一個明顯特征:在我們客戶端代碼工作前,被代理對象的代碼已知(是由程序員自己開發或者第三方自動生成),通俗來說就是在編碼階段我們就已經知道了這個代理類將代理誰。,這就是靜態代理

2、動態代理

而動態代理則與靜態代理相反,在編碼階段我們不知道被代理對象是誰,被代理對象是在運行階段動態生成的(一般是通過反射機制),動態代理的實現十分簡單,因為JDK 自身已經為我提供了一個便捷的動態代理接口InvocationHandler,我們只需要實現該接口并重寫invoke方法即可,前面步驟和靜態代理差不多(略),區別在代理類的實現。

  • 實現動態代理接口InvocationHandler
  • 持有被代理對象的引用,由于被代理對象未知,所以使用Object類型
  • 通過構造方法傳遞被代理對象的引用
  • 重寫invoke方法,并在方法中通過反射調用被代理類的方法
package proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class DynamicProxyer implements InvocationHandler {
    private Object obj;//被代理對象的引用

    
    public DynamicProxyer(Object obj) {
        this.obj = obj;
    }

    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        Object result=method.invoke(obj, args);//調用被代理對象的方法
        return null;
    }
}

測試

import java.lang.reflect.Proxy;

public class DynamicClient {

    /**
     * @param args
     */
    public static void main(String[] args) {
        ILegalSuit suiter=new LegalSuiter();//構造一個被代理對象
        
        DynamicProxyer dynaProxyer=new DynamicProxyer(suiter);//構造一個動態代理
        ClassLoader loader=suiter.getClass().getClassLoader();
        //動態構造一個具體的代理對象,在這綁定上了被代理對象
        ILegalSuit dynaSuiter=(ILegalSuit) Proxy.newProxyInstance(loader, new Class[]{ILegalSuit.class}, dynaProxyer);
        dynaSuiter.submit();
        dynaSuiter.proof();
        dynaSuiter.debate();
    }

}

由以上例子不難得出,動態代理其實是同一個動態代理類來來代理N多個不同的代理類,為的就是對被代理者和代理者解耦,而靜態代理這是只能在開發階段就決定了被代理者。

1、遠程代理

為位于兩個不同地址空間對象的訪問提供了一種實現機制,可以將一些消耗資源較多的對象和操作移至性能更好的計算機上,提高系統的整體運行效率。

2、虛擬代理

虛擬代理的基本思想其實和延遲初始化相似,如果需要創建一個資源消耗較大的復雜對象,先創建一個小的對象來表示,等到真正需要用的時候才會創建真實的對象。當用戶請求一個大的對象時候,虛擬代理暫時充當真實對象的角色,待該真實對象真正被創建出來之后,虛擬代理就會將用戶請求委托給真是對象。簡而言之,使用一個代理對象暫時表示一個消耗巨大資源的真實對象,而真是對象僅僅在真正使用的時候才被創建
虛擬代理模式(Virtual Proxy)是一種節省內存的技術,它建議創建那些占用大量內存或處理復雜的對象時,把創建這類對象推遲到使用它的時候。在特定的應用中,不同部分的功能由不同的對象組成,應用啟動的時候,不會立即使用所有的對象。在這種情況下,虛擬代理模式建議推遲對象的創建直到應用程序需要它為止。對象被應用第一次引用時創建并且同一個實例可以被重用。這種方法優缺點并存。

2.1、虛擬代理的優點

這種方法的優點是,在應用程序啟動時,由于不需要創建和裝載所有的對象,因此加速了應用程序的啟動。

2.1、虛擬代理的缺點

因為不能保證特定的應用程序對象被創建,在訪問這個對象的任何地方,都需要檢測確認它不是空(null)。也就是,這種檢測的時間消耗是最大的缺點。

2.3、虛擬代理的使用逐一實現

應用虛擬代理模式,需要設計一個與真實對象具有相同接口的單獨對象(指虛擬代理)。不同的客戶對象可以在創建和使用真實對象地方用相應的虛擬對象來代替。虛擬對象把真實對象的引用作為它的實例變量維護。代理對象不要自動創建真實對象,當客戶需要真實對象的服務時,調用虛擬代理對象上的方法,并且檢測真實對象是否被創建。如果真實對象已經創建,代理把調用轉發給真實對象,如果真實對象沒有被創建:

  • 代理對象創建真實對象

  • 代理對象把這個對象分配給引用變量。

  • 代理把調用轉發給真實對象

按照這種安排,驗證對象存在和轉發方法調用這些細節對于客戶是不可見的。客戶對象就像和真實對象一樣與代理對象進行交互。因此客戶從檢測真實對象是否為null中解脫出來,另外,由于創建代理對象在時間和處理復雜度上要少于創建真實對象。因此,在應用程序啟動的時候,用代理對象代替真實對象初始化。

3、保護代理

原始對象有不同的訪問權限時,使用代理控制對原始對象的訪問。可以控制對一個對象的訪問權限,為不同用戶提供不同級別的使用權限。

4、智能引用

在訪問原始對象時執行一些代理自己附加的操作并對原始對象的引用計數

未完待續...

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,431評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,637評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,555評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,900評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,629評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,976評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,976評論 3 448
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,139評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,686評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,411評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,641評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,129評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,820評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,233評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,567評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,362評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,604評論 2 380

推薦閱讀更多精彩內容

  • 設計模式匯總 一、基礎知識 1. 設計模式概述 定義:設計模式(Design Pattern)是一套被反復使用、多...
    MinoyJet閱讀 3,961評論 1 15
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,828評論 18 139
  • 目錄 本文的結構如下: 引言 什么是代理模式 模式的結構 典型代碼 代理模式分類 代碼示例 代理模式和裝飾者模式的...
    w1992wishes閱讀 1,563評論 0 13
  • 包是包子,坨是大坨,兩者都是家鄉的極致美味。以往,每年只有過年才能吃上一次,一次的記憶回味一年,堪比山珍。 現在不...
    圓圓2408閱讀 507評論 0 3
  • 在這個龐大的霧霾之都迎來了八月的最后一天。意味著九月即將開始,工作上的很多事情都要走上正軌了。在這樣一個周末,正好...
    鹿角海棠閱讀 300評論 0 1