結(jié)構(gòu)型SEQ7 - 代理模式 Proxy Pattern

【學(xué)習(xí)難度:★★★☆☆嗜暴,使用頻率:★★★★☆】
直接出處:代理模式
梳理和學(xué)習(xí):https://github.com/BruceOuyang/boy-design-pattern
簡(jiǎn)書(shū)日期: 2018/03/15
簡(jiǎn)書(shū)首頁(yè):http://www.reibang.com/p/0fb891a7c5ed

設(shè)計(jì)模式之代理模式(一)

代理模式是常用的結(jié)構(gòu)型設(shè)計(jì)模式之一膝蜈,當(dāng)無(wú)法直接訪問(wèn)某個(gè)對(duì)象或訪問(wèn)某個(gè)對(duì)象存在困難時(shí)可以通過(guò)一個(gè)代理對(duì)象來(lái)間接訪問(wèn)懦铺,為了保證客戶端使用的透明性奔穿,所訪問(wèn)的真實(shí)對(duì)象與代理對(duì)象需要實(shí)現(xiàn)相同的接口。根據(jù)代理模式的使用目的不同远搪,代理模式又可以分為多種類型永毅,例如保護(hù)代理、遠(yuǎn)程代理丧没、虛擬代理鹰椒、緩沖代理等,它們應(yīng)用于不同的場(chǎng)合骂铁,滿足用戶的不同需求吹零。

15.1 代理模式概述

近年來(lái)罩抗,代購(gòu)已逐步成為電子商務(wù)的一個(gè)重要分支拉庵。何謂代購(gòu)镰烧,簡(jiǎn)單來(lái)說(shuō)就是找人幫忙購(gòu)買(mǎi)所需要的商品叹卷,當(dāng)然你可能需要向?qū)嵤┐?gòu)的人支付一定的費(fèi)用。代購(gòu)?fù)ǔ7譃閮煞N類型:一種是因?yàn)樵诋?dāng)?shù)刭I(mǎi)不到某件商品魁蒜,又或者是因?yàn)楫?dāng)?shù)剡@件商品的價(jià)格比其他地區(qū)的貴操刀,因此托人在其他地區(qū)甚至國(guó)外購(gòu)買(mǎi)該商品烁挟,然后通過(guò)快遞發(fā)貨或者直接攜帶回來(lái);還有一種代購(gòu)骨坑,由于消費(fèi)者對(duì)想要購(gòu)買(mǎi)的商品相關(guān)信息的缺乏撼嗓,自已無(wú)法確定其實(shí)際價(jià)值而又不想被商家宰,只好委托中介機(jī)構(gòu)幫其講價(jià)或?yàn)槠浯I(mǎi)欢唾。代購(gòu)網(wǎng)站為此應(yīng)運(yùn)而生且警,它為消費(fèi)者提供在線的代購(gòu)服務(wù),如果看中某國(guó)外購(gòu)物網(wǎng)站上的商品礁遣,可以登錄代購(gòu)網(wǎng)站填寫(xiě)代購(gòu)單并付款斑芜,代購(gòu)網(wǎng)站會(huì)幫助進(jìn)行購(gòu)買(mǎi)然后通過(guò)快遞公司將商品發(fā)送給消費(fèi)者。商品代購(gòu)過(guò)程如圖15-1所示:

圖15-1 商品代購(gòu)示意圖

在軟件開(kāi)發(fā)中祟霍,也有一種設(shè)計(jì)模式可以提供與代購(gòu)網(wǎng)站類似的功能杏头。由于某些原因,客戶端不想或不能直接訪問(wèn)一個(gè)對(duì)象沸呐,此時(shí)可以通過(guò)一個(gè)稱之為“代理”的第三者來(lái)實(shí)現(xiàn)間接訪問(wèn)醇王,該方案對(duì)應(yīng)的設(shè)計(jì)模式被稱為代理模式。

代理模式是一種應(yīng)用很廣泛的結(jié)構(gòu)型設(shè)計(jì)模式崭添,而且變化形式非常多寓娩,常見(jiàn)的代理形式包括遠(yuǎn)程代理、保護(hù)代理、虛擬代理根暑、緩沖代理力试、智能引用代理等,后面將學(xué)習(xí)這些不同的代理形式排嫌。

代理模式定義如下:

代理模式:給某一個(gè)對(duì)象提供一個(gè)代理或占位符畸裳,并由代理對(duì)象來(lái)控制對(duì)原對(duì)象的訪問(wèn)。 Proxy Pattern: Provide a surrogate or placeholder for another object to control access to it.

代理模式是一種對(duì)象結(jié)構(gòu)型模式淳地。在代理模式中引入了一個(gè)新的代理對(duì)象怖糊,代理對(duì)象在客戶端對(duì)象和目標(biāo)對(duì)象之間起到中介的作用,它去掉客戶不能看到的內(nèi)容和服務(wù)或者增添客戶需要的額外的新服務(wù)颇象。

15.2 代理模式結(jié)構(gòu)與實(shí)現(xiàn)

15.2.1 模式結(jié)構(gòu)

代理模式的結(jié)構(gòu)比較簡(jiǎn)單伍伤,其核心是代理類,為了讓客戶端能夠一致性地對(duì)待真實(shí)對(duì)象和代理對(duì)象遣钳,在代理模式中引入了抽象層扰魂,代理模式結(jié)構(gòu)如圖15-2所示:

圖15-2 代理模式結(jié)構(gòu)圖

由圖15-2可知,代理模式包含如下三個(gè)角色:

(1) Subject(抽象主題角色):它聲明了真實(shí)主題和代理主題的共同接口蕴茴,這樣一來(lái)在任何使用真實(shí)主題的地方都可以使用代理主題劝评,客戶端通常需要針對(duì)抽象主題角色進(jìn)行編程。

(2) Proxy(代理主題角色):它包含了對(duì)真實(shí)主題的引用倦淀,從而可以在任何時(shí)候操作真實(shí)主題對(duì)象蒋畜;在代理主題角色中提供一個(gè)與真實(shí)主題角色相同的接口,以便在任何時(shí)候都可以替代真實(shí)主題撞叽;代理主題角色還可以控制對(duì)真實(shí)主題的使用姻成,負(fù)責(zé)在需要的時(shí)候創(chuàng)建和刪除真實(shí)主題對(duì)象,并對(duì)真實(shí)主題對(duì)象的使用加以約束愿棋。通常科展,在代理主題角色中,客戶端在調(diào)用所引用的真實(shí)主題操作之前或之后還需要執(zhí)行其他操作初斑,而不僅僅是單純調(diào)用真實(shí)主題對(duì)象中的操作辛润。

(3) RealSubject(真實(shí)主題角色):它定義了代理角色所代表的真實(shí)對(duì)象,在真實(shí)主題角色中實(shí)現(xiàn)了真實(shí)的業(yè)務(wù)操作见秤,客戶端可以通過(guò)代理主題角色間接調(diào)用真實(shí)主題角色中定義的操作砂竖。

15.2.2 模式實(shí)現(xiàn)

代理模式的結(jié)構(gòu)圖比較簡(jiǎn)單,但是在真實(shí)的使用和實(shí)現(xiàn)過(guò)程中要復(fù)雜很多鹃答,特別是代理類的設(shè)計(jì)和實(shí)現(xiàn)乎澄。

抽象主題類聲明了真實(shí)主題類和代理類的公共方法,它可以是接口测摔、抽象類或具體類置济,客戶端針對(duì)抽象主題類編程解恰,一致性地對(duì)待真實(shí)主題和代理主題,典型的抽象主題類代碼如下:

public abstract class Subject  
{  
    public abstract void request();  
}

真實(shí)主題類繼承了抽象主題類浙于,提供了業(yè)務(wù)方法的具體實(shí)現(xiàn)护盈,其典型代碼如下:

public class RealSubject extends Subject  
{  
    @Override
    public void request()  
    {  
        //業(yè)務(wù)方法具體實(shí)現(xiàn)代碼  
    }  
}

代理類也是抽象主題類的子類,它維持一個(gè)對(duì)真實(shí)主題對(duì)象的引用羞酗,調(diào)用在真實(shí)主題中實(shí)現(xiàn)的業(yè)務(wù)方法腐宋,在調(diào)用時(shí)可以在原有業(yè)務(wù)方法的基礎(chǔ)上附加一些新的方法來(lái)對(duì)功能進(jìn)行擴(kuò)充或約束,最簡(jiǎn)單的代理類實(shí)現(xiàn)代碼如下:

class Proxy extends Subject  
{  
    /**
     * 維持一個(gè)對(duì)真實(shí)主題對(duì)象的引用 
     */
    private RealSubject realSubject = new RealSubject();   

    public void preRequest()   
    {
        //...  
    }  

    @Override
    public void request()   
    {  
        preRequest();  
        
        // 調(diào)用真實(shí)主題對(duì)象的方法 
        realSubject.request();  
        
        postRequest();  
    }  

    public void PostRequest()   
    {  
        // …  
    }  
}

在實(shí)際開(kāi)發(fā)過(guò)程中檀轨,代理類的實(shí)現(xiàn)比上述代碼要復(fù)雜很多胸竞,代理模式根據(jù)其目的和實(shí)現(xiàn)方式不同可分為很多種類,其中常用的幾種代理模式簡(jiǎn)要說(shuō)明如下:

(1) 遠(yuǎn)程代理(Remote Proxy):為一個(gè)位于不同的地址空間的對(duì)象提供一個(gè)本地的代理對(duì)象参萄,這個(gè)不同的地址空間可以是在同一臺(tái)主機(jī)中卫枝,也可是在另一臺(tái)主機(jī)中,遠(yuǎn)程代理又稱為大使(Ambassador)讹挎。

(2) 虛擬代理(Virtual Proxy):如果需要?jiǎng)?chuàng)建一個(gè)資源消耗較大的對(duì)象校赤,先創(chuàng)建一個(gè)消耗相對(duì)較小的對(duì)象來(lái)表示,真實(shí)對(duì)象只在需要時(shí)才會(huì)被真正創(chuàng)建淤袜。

(3) 保護(hù)代理(Protect Proxy):控制對(duì)一個(gè)對(duì)象的訪問(wèn)痒谴,可以給不同的用戶提供不同級(jí)別的使用權(quán)限衰伯。

(4) 緩沖代理(Cache Proxy):為某一個(gè)目標(biāo)操作的結(jié)果提供臨時(shí)的存儲(chǔ)空間铡羡,以便多個(gè)客戶端可以共享這些結(jié)果。

(5) 智能引用代理(Smart Reference Proxy):當(dāng)一個(gè)對(duì)象被引用時(shí)意鲸,提供一些額外的操作烦周,例如將對(duì)象被調(diào)用的次數(shù)記錄下來(lái)等。

在這些常用的代理模式中怎顾,有些代理類的設(shè)計(jì)非常復(fù)雜读慎,例如遠(yuǎn)程代理類,它封裝了底層網(wǎng)絡(luò)通信和對(duì)遠(yuǎn)程對(duì)象的調(diào)用槐雾,其實(shí)現(xiàn)較為復(fù)雜夭委。

設(shè)計(jì)模式之代理模式(二)

15.3 代理模式應(yīng)用實(shí)例

下面通過(guò)一個(gè)應(yīng)用實(shí)例來(lái)進(jìn)一步學(xué)習(xí)和理解代理模式。

15.3.1 實(shí)例說(shuō)明

某軟件公司承接了某信息咨詢公司的收費(fèi)商務(wù)信息查詢系統(tǒng)的開(kāi)發(fā)任務(wù)募强,該系統(tǒng)的基本需求如下:

(1) 在進(jìn)行商務(wù)信息查詢之前用戶需要通過(guò)身份驗(yàn)證株灸,只有合法用戶才能夠使用該查詢系統(tǒng);

(2) 在進(jìn)行商務(wù)信息查詢時(shí)系統(tǒng)需要記錄查詢?nèi)罩厩嬷担员愀鶕?jù)查詢次數(shù)收取查詢費(fèi)用慌烧。

該軟件公司開(kāi)發(fā)人員已完成了商務(wù)信息查詢模塊的開(kāi)發(fā)任務(wù),現(xiàn)希望能夠以一種松耦合的方式向原有系統(tǒng)增加身份驗(yàn)證和日志記錄功能鸠儿,客戶端代碼可以無(wú)區(qū)別地對(duì)待原始的商務(wù)信息查詢模塊和增加新功能之后的商務(wù)信息查詢模塊屹蚊,而且可能在將來(lái)還要在該信息查詢模塊中增加一些新的功能厕氨。

試使用代理模式設(shè)計(jì)并實(shí)現(xiàn)該收費(fèi)商務(wù)信息查詢系統(tǒng)。

15.3.2 實(shí)例分析及類圖

通過(guò)分析汹粤,可以采用一種間接訪問(wèn)的方式來(lái)實(shí)現(xiàn)該商務(wù)信息查詢系統(tǒng)的設(shè)計(jì)命斧,在客戶端對(duì)象和信息查詢對(duì)象之間增加一個(gè)代理對(duì)象,讓代理對(duì)象來(lái)實(shí)現(xiàn)身份驗(yàn)證和日志記錄等功能嘱兼,而無(wú)須直接對(duì)原有的商務(wù)信息查詢對(duì)象進(jìn)行修改冯丙,如圖15-3所示:

圖15-3 商務(wù)信息查詢系統(tǒng)設(shè)計(jì)方案示意圖

在圖15-3中,客戶端對(duì)象通過(guò)代理對(duì)象間接訪問(wèn)具有商務(wù)信息查詢功能的真實(shí)對(duì)象遭京,在代理對(duì)象中除了調(diào)用真實(shí)對(duì)象的商務(wù)信息查詢功能外胃惜,還增加了身份驗(yàn)證和日志記錄等功能。使用代理模式設(shè)計(jì)該商務(wù)信息查詢系統(tǒng)哪雕,結(jié)構(gòu)圖如圖15-4所示船殉。

圖15-4 商務(wù)信息查詢系統(tǒng)結(jié)構(gòu)圖

在圖15-4中,業(yè)務(wù)類AccessValidator用于驗(yàn)證用戶身份斯嚎,業(yè)務(wù)類Logger用于記錄用戶查詢?nèi)罩纠妫琒earcher充當(dāng)抽象主題角色,RealSearcher充當(dāng)真實(shí)主題角色堡僻,ProxySearcher充當(dāng)代理主題角色糠惫。

15.3.3 實(shí)例代碼

(1) AccessValidator:身份驗(yàn)證類,業(yè)務(wù)類钉疫,它提供方法Validate()來(lái)實(shí)現(xiàn)身份驗(yàn)證硼讽。

class AccessValidator  
{  
    //模擬實(shí)現(xiàn)登錄驗(yàn)證  
    public boolean validate(string userId)   
    {  
        System.out.println("在數(shù)據(jù)庫(kù)中驗(yàn)證用戶'" + userId + "'是否是合法用戶?");  
        if (userId.equals("楊過(guò)")) {  
            System.out.println("'{0}'登錄成功牲阁!",userId);  
            return true;  
        }  
        else {  
            System.out.println("'{0}'登錄失敼谈蟆!", userId);  
            return false;  
        }  
    }  
}  

(2) Logger:日志記錄類城菊,業(yè)務(wù)類备燃,它提供方法Log()來(lái)保存日志。

class Logger  
{  
    //模擬實(shí)現(xiàn)日志記錄  
    public void log(string userId) {  
        System.out.println("更新數(shù)據(jù)庫(kù)凌唬,用戶'{0}'查詢次數(shù)+1并齐!",userId);  
    }  
} 

(3) Searcher:抽象查詢類,充當(dāng)抽象主題角色客税,它聲明了DoSearch()方法况褪。

interface Searcher  
{  
    String doSearch(string userId, string keyword);  
}  

(4) RealSearcher:具體查詢類,充當(dāng)真實(shí)主題角色霎挟,它實(shí)現(xiàn)查詢功能窝剖,提供方法DoSearch()來(lái)查詢信息。

class RealSearcher implements Searcher  
{  
    //模擬查詢商務(wù)信息  
    public string doSearch(string userId, string keyword) {  
        System.out.println("用戶'{0}'使用關(guān)鍵詞'{1}'查詢商務(wù)信息酥夭!",userId,keyword);  
        return "返回具體內(nèi)容";  
    }  
}

(5) ProxySearcher:代理查詢類赐纱,充當(dāng)代理主題角色脊奋,它是查詢代理,維持了對(duì)RealSearcher對(duì)象疙描、AccessValidator對(duì)象和Logger對(duì)象的引用诚隙。

class ProxySearcher implements Searcher  
{  
    /**
     * 維持一個(gè)對(duì)真實(shí)主題的引用
     */
    private RealSearcher searcher = new RealSearcher();   
    private AccessValidator validator;  
    private Logger logger;  

    public string doSearch(string userId, string keyword)  
    {  
        //如果身份驗(yàn)證成功,則執(zhí)行查詢  
        if (this.validate(userId))  
        {  
            string result = searcher.doSearch(userId, keyword); //調(diào)用真實(shí)主題對(duì)象的查詢方法  
            this.Log(userId); //記錄查詢?nèi)罩? 
            return result; //返回查詢結(jié)果  
        }  
        else  
        {  
            return null;  
        }  
    }  

    //創(chuàng)建訪問(wèn)驗(yàn)證對(duì)象并調(diào)用其Validate()方法實(shí)現(xiàn)身份驗(yàn)證  
    public bool validate(string userId)  
    {  
        validator = new AccessValidator();  
        return validator.Validate(userId);  
    }  

    //創(chuàng)建日志記錄對(duì)象并調(diào)用其Log()方法實(shí)現(xiàn)日志記錄  
    public void log(string userId)  
    {  
        logger = new Logger();  
        logger.Log(userId);  
    }  
} 

(6) 配置文件App.config起胰,在配置文件中存儲(chǔ)了代理主題類類名久又。

<?xml version="1.0" encoding="utf-8" ?>  
<configuration>  
  <appSettings>  
    <add key="proxy" value="ProxySample.ProxySearcher"/>  
  </appSettings>  
</configuration>

(7) Client:客戶端測(cè)試類

class Client  
{  
    public static void main(String[] args)  
    {  
        //讀取配置文件  
        String proxy = ConfigurationManager.AppSettings["proxy"];  

        //反射生成對(duì)象,針對(duì)抽象編程效五,客戶端無(wú)須分辨真實(shí)主題類和代理類  
        Searcher searcher = (Searcher)ProxyXMLUtil.getBean();  

        String result = searcher.doSearch("楊過(guò)", "玉女心經(jīng)");   
    }  
} 

15.3.4 結(jié)果及分析

編譯并運(yùn)行程序地消,輸出結(jié)果如下:

在數(shù)據(jù)庫(kù)中驗(yàn)證用戶'楊過(guò)'是否是合法用戶?
'楊過(guò)'登錄成功畏妖!
用戶'楊過(guò)'使用關(guān)鍵詞'玉女心經(jīng)'查詢商務(wù)信息脉执!
更新數(shù)據(jù)庫(kù),用戶'楊過(guò)'查詢次數(shù)加1戒劫!

本實(shí)例是保護(hù)代理和智能引用代理的應(yīng)用實(shí)例半夷,在代理類ProxySearcher中實(shí)現(xiàn)對(duì)真實(shí)主題類的權(quán)限控制和引用計(jì)數(shù),如果需要在訪問(wèn)真實(shí)主題時(shí)增加新的訪問(wèn)控制機(jī)制和新功能迅细,只需增加一個(gè)新的代理類巫橄,再修改配置文件,在客戶端代碼中使用新增代理類即可茵典,源代碼無(wú)須修改湘换,符合開(kāi)閉原則。

設(shè)計(jì)模式之代理模式(三)

15.4 遠(yuǎn)程代理

遠(yuǎn)程代理(Remote Proxy)是一種常用的代理模式敬尺,它使得客戶端程序可以訪問(wèn)在遠(yuǎn)程主機(jī)上的對(duì)象枚尼,遠(yuǎn)程主機(jī)可能具有更好的計(jì)算性能與處理速度,可以快速響應(yīng)并處理客戶端的請(qǐng)求砂吞。遠(yuǎn)程代理可以將網(wǎng)絡(luò)的細(xì)節(jié)隱藏起來(lái),使得客戶端不必考慮網(wǎng)絡(luò)的存在崎溃◎咧保客戶端完全可以認(rèn)為被代理的遠(yuǎn)程業(yè)務(wù)對(duì)象是在本地而不是在遠(yuǎn)程,而遠(yuǎn)程代理對(duì)象承擔(dān)了大部分的網(wǎng)絡(luò)通信工作袁串,并負(fù)責(zé)對(duì)遠(yuǎn)程業(yè)務(wù)方法的調(diào)用概而。

遠(yuǎn)程代理示意圖如圖15-5所示,客戶端對(duì)象不能直接訪問(wèn)遠(yuǎn)程主機(jī)中的業(yè)務(wù)對(duì)象囱修,只能采取間接訪問(wèn)的方式赎瑰。遠(yuǎn)程業(yè)務(wù)對(duì)象在本地主機(jī)中有一個(gè)代理對(duì)象,該代理對(duì)象負(fù)責(zé)對(duì)遠(yuǎn)程業(yè)務(wù)對(duì)象的訪問(wèn)和網(wǎng)絡(luò)通信破镰,它對(duì)于客戶端對(duì)象而言是透明的餐曼⊙勾ⅲ客戶端無(wú)須關(guān)心實(shí)現(xiàn)具體業(yè)務(wù)的是誰(shuí),只需要按照服務(wù)接口所定義的方式直接與本地主機(jī)中的代理對(duì)象交互即可源譬。

圖15-5 遠(yuǎn)程代理示意圖

擴(kuò)展

在基于.NET平臺(tái)的分布式技術(shù)集惋,例如DCOM(Distribute Component Object Model,分布式組件對(duì)象模型)踩娘、Web Service中刮刑,都應(yīng)用了遠(yuǎn)程代理模式,大家可以查閱相關(guān)資料進(jìn)行擴(kuò)展學(xué)習(xí)养渴。

15.5 虛擬代理

虛擬代理(Virtual Proxy)也是一種常用的代理模式雷绢,對(duì)于一些占用系統(tǒng)資源較多或者加載時(shí)間較長(zhǎng)的對(duì)象,可以給這些對(duì)象提供一個(gè)虛擬代理理卑。在真實(shí)對(duì)象創(chuàng)建成功之前虛擬代理扮演真實(shí)對(duì)象的替身习寸,而當(dāng)真實(shí)對(duì)象創(chuàng)建之后,虛擬代理將用戶的請(qǐng)求轉(zhuǎn)發(fā)給真實(shí)對(duì)象傻工。

通常霞溪,在以下兩種情況下可以考慮使用虛擬代理:

(1) 由于對(duì)象本身的復(fù)雜性或者網(wǎng)絡(luò)等原因?qū)е乱粋€(gè)對(duì)象需要較長(zhǎng)的加載時(shí)間,此時(shí)可以用一個(gè)加載時(shí)間相對(duì)較短的代理對(duì)象來(lái)代表真實(shí)對(duì)象中捆。通常在實(shí)現(xiàn)時(shí)可以結(jié)合多線程技術(shù)鸯匹,一個(gè)線程用于顯示代理對(duì)象,其他線程用于加載真實(shí)對(duì)象泄伪。這種虛擬代理模式可以應(yīng)用在程序啟動(dòng)的時(shí)候殴蓬,由于創(chuàng)建代理對(duì)象在時(shí)間和處理復(fù)雜度上要少于創(chuàng)建真實(shí)對(duì)象,因此蟋滴,在程序啟動(dòng)時(shí)染厅,可以用代理對(duì)象代替真實(shí)對(duì)象初始化,大大加速了系統(tǒng)的啟動(dòng)時(shí)間津函。當(dāng)需要使用真實(shí)對(duì)象時(shí)肖粮,再通過(guò)代理對(duì)象來(lái)引用,而此時(shí)真實(shí)對(duì)象可能已經(jīng)成功加載完畢尔苦,可以縮短用戶的等待時(shí)間涩馆。

(2) 當(dāng)一個(gè)對(duì)象的加載十分耗費(fèi)系統(tǒng)資源的時(shí)候,也非常適合使用虛擬代理允坚。虛擬代理可以讓那些占用大量?jī)?nèi)存或處理起來(lái)非常復(fù)雜的對(duì)象推遲到使用它們的時(shí)候才創(chuàng)建魂那,而在此之前用一個(gè)相對(duì)來(lái)說(shuō)占用資源較少的代理對(duì)象來(lái)代表真實(shí)對(duì)象,再通過(guò)代理對(duì)象來(lái)引用真實(shí)對(duì)象稠项。為了節(jié)省內(nèi)存涯雅,在第一次引用真實(shí)對(duì)象時(shí)再創(chuàng)建對(duì)象,并且該對(duì)象可被多次重用展运,在以后每次訪問(wèn)時(shí)需要檢測(cè)所需對(duì)象是否已經(jīng)被創(chuàng)建活逆,因此在訪問(wèn)該對(duì)象時(shí)需要進(jìn)行存在性檢測(cè)精刷,這需要消耗一定的系統(tǒng)時(shí)間,但是可以節(jié)省內(nèi)存空間划乖,這是一種用時(shí)間換取空間的做法贬养。

無(wú)論是以上哪種情況,虛擬代理都是用一個(gè)“虛假”的代理對(duì)象來(lái)代表真實(shí)對(duì)象琴庵,通過(guò)代理對(duì)象來(lái)間接引用真實(shí)對(duì)象误算,可以在一定程度上提高系統(tǒng)的性能。

15.6 緩沖代理

緩沖代理(Cache Proxy)也是一種較為常用的代理模式迷殿,它為某一個(gè)操作的結(jié)果提供臨時(shí)的緩存存儲(chǔ)空間儿礼,以便在后續(xù)使用中能夠共享這些結(jié)果,從而可以避免某些方法的重復(fù)執(zhí)行庆寺,優(yōu)化系統(tǒng)性能蚊夫。

在微軟示例項(xiàng)目PetShop 4.0的業(yè)務(wù)邏輯層(Business Logic Layer, BLL)中定義了Product、Category懦尝、Item等類知纷,它們封裝了相關(guān)的業(yè)務(wù)方法,用于調(diào)用數(shù)據(jù)訪問(wèn)層(Data Access Layer, DAL)對(duì)象訪問(wèn)數(shù)據(jù)庫(kù)陵霉,以獲取相關(guān)數(shù)據(jù)琅轧。為了改進(jìn)系統(tǒng)性能,PetShop 4.0為這些實(shí)現(xiàn)方法增加緩存機(jī)制踊挠,引入一個(gè)新的對(duì)象去控制原來(lái)的BLL業(yè)務(wù)邏輯對(duì)象乍桂,這些新的對(duì)象對(duì)應(yīng)于代理模式中的代理對(duì)象。在引入代理模式后效床,實(shí)現(xiàn)了在緩存級(jí)別上對(duì)業(yè)務(wù)對(duì)象的封裝睹酌,增強(qiáng)了對(duì)業(yè)務(wù)對(duì)象的控制,如果需要訪問(wèn)的數(shù)據(jù)在緩存中已經(jīng)存在剩檀,則無(wú)須再重復(fù)執(zhí)行獲取數(shù)據(jù)的方法憋沿,直接返回存儲(chǔ)在緩存中的數(shù)據(jù)即可。由于原有業(yè)務(wù)對(duì)象(真實(shí)對(duì)象)和新增代理對(duì)象暴露在外的方法是一致的谨朝,因而對(duì)于調(diào)用方即客戶端而言卤妒,調(diào)用代理對(duì)象與真實(shí)對(duì)象并沒(méi)有實(shí)質(zhì)的區(qū)別。

這些新引入的代理類包括ProductDataProxy字币、CategoryDataProxy和ItemDataProxy等。下面以PetShop.BLL.Product業(yè)務(wù)對(duì)象為例進(jìn)行說(shuō)明共缕,PetShop 4.0為其建立了代理對(duì)象ProductDataProxy洗出,并在ProductDataProxy的GetProductsByCategory()方法中調(diào)用了業(yè)務(wù)邏輯層Product類的GetProductsByCategory()方法,同時(shí)增加了緩存機(jī)制图谷。如圖15-6所示:

圖15-6 PetShop4.0緩存代理示意圖

在ProductDataProxy類中存在如下代碼片段:

public static class ProductDataProxy  
{  
    private static readonly int productTimeout = int.Parse(ConfigurationManager.AppSettings ["ProductCacheDuration"]);  
    private static readonly bool enableCaching = bool.Parse(ConfigurationManager. AppSettings["EnableCaching"]);   

    public static IList GetProductsByCategory(string category)  
    {          
        Product product = new Product();  

        //如果緩存被禁用翩活,則直接通過(guò)product對(duì)象來(lái)獲取數(shù)據(jù)  
         if (!enableCaching)  
        {  
            return product.GetProductsByCategory(category);  
        }  

        string key = "product_by_category_" + category;  
        //從緩存中獲取數(shù)據(jù)  
         IList data = (IList )HttpRuntime.Cache[key];    

        //如果緩存中沒(méi)有數(shù)據(jù)則執(zhí)行如下代碼  
          if (data == null)  
        {              
          data = product.GetProductsByCategory(category);              
          //通過(guò)工廠創(chuàng)建AggregateCacheDependency對(duì)象  
            AggregateCacheDependency cd = DependencyFacade.GetProductDependency ();   
          //將數(shù)據(jù)存儲(chǔ)在緩存中阱洪,并添加必要的AggregateCacheDependency對(duì)象  
            HttpRuntime.Cache.Add(key, data, cd, DateTime.Now.AddHours(product Timeout), Cache.NoSlidingExpiration, CacheItemPriority.High, null);   
        }  
        return data;  
    }  
    // ……  
}

在上述代碼中,AggregateCacheDependency是從.NET Framework 2.0開(kāi)始新增的一個(gè)類菠镇,它負(fù)責(zé)監(jiān)視依賴項(xiàng)對(duì)象的集合冗荸。當(dāng)這個(gè)集合中的任意一個(gè)依賴項(xiàng)對(duì)象發(fā)生改變時(shí),該依賴項(xiàng)對(duì)象對(duì)應(yīng)的緩存對(duì)象都將被自動(dòng)移除利耍。在此不對(duì)AggregateCacheDependency進(jìn)行詳細(xì)說(shuō)明蚌本,大家可以查閱相關(guān)資料進(jìn)行擴(kuò)展學(xué)習(xí)。
與業(yè)務(wù)邏輯層Product對(duì)象的GetProductsByCategory()方法相比隘梨,上述代碼增加了緩存機(jī)制程癌。當(dāng)緩存內(nèi)不存在相關(guān)數(shù)據(jù)項(xiàng)時(shí),則直接調(diào)用業(yè)務(wù)邏輯層Product的GetProductsByCategory()方法來(lái)獲取數(shù)據(jù)轴猎,并將其與對(duì)應(yīng)的AggregateCacheDependency對(duì)象一起存儲(chǔ)在緩存中嵌莉。在ProductDataProxy類的每一個(gè)業(yè)務(wù)方法中都實(shí)例化了Product類,再調(diào)用Product類的相應(yīng)方法捻脖,因此ProductDataProxy與Product之間屬于依賴關(guān)系锐峭,這是標(biāo)準(zhǔn)代理模式的一種變形,可以按照標(biāo)準(zhǔn)代理模式對(duì)其進(jìn)行改進(jìn)可婶,包括引入高層的抽象接口沿癞。

設(shè)計(jì)模式之代理模式(四)

15.7 代理模式效果與適用場(chǎng)景

代理模式是常用的結(jié)構(gòu)型設(shè)計(jì)模式之一,它為對(duì)象的間接訪問(wèn)提供了一個(gè)解決方案扰肌,可以對(duì)對(duì)象的訪問(wèn)進(jìn)行控制抛寝。代理模式類型較多,其中遠(yuǎn)程代理曙旭、虛擬代理盗舰、保護(hù)代理等在軟件開(kāi)發(fā)中應(yīng)用非常廣泛。

15.7.1 模式優(yōu)點(diǎn)

代理模式的共同優(yōu)點(diǎn)如下:

(1) 能夠協(xié)調(diào)調(diào)用者和被調(diào)用者桂躏,在一定程度上降低了系統(tǒng)的耦合度钻趋。

(2) 客戶端可以針對(duì)抽象主題角色進(jìn)行編程,增加和更換代理類無(wú)須修改源代碼剂习,符合開(kāi)閉原則蛮位,系統(tǒng)具有較好的靈活性和可擴(kuò)展性。

此外鳞绕,不同類型的代理模式也具有獨(dú)特的優(yōu)點(diǎn)失仁,例如:

(1) 遠(yuǎn)程代理為位于兩個(gè)不同地址空間對(duì)象的訪問(wèn)提供了一種實(shí)現(xiàn)機(jī)制,可以將一些消耗資源較多的對(duì)象和操作移至性能更好的計(jì)算機(jī)上们何,提高系統(tǒng)的整體運(yùn)行效率萄焦。

(2) 虛擬代理通過(guò)一個(gè)消耗資源較少的對(duì)象來(lái)代表一個(gè)消耗資源較多的對(duì)象,可以在一定程度上節(jié)省系統(tǒng)的運(yùn)行開(kāi)銷。

(3) 緩沖代理為某一個(gè)操作的結(jié)果提供臨時(shí)的緩存存儲(chǔ)空間拂封,以便在后續(xù)使用中能夠共享這些結(jié)果茬射,優(yōu)化系統(tǒng)性能,縮短執(zhí)行時(shí)間冒签。

(4) 保護(hù)代理可以控制對(duì)一個(gè)對(duì)象的訪問(wèn)權(quán)限在抛,為不同用戶提供不同級(jí)別的使用權(quán)限。

15.7.2 模式缺點(diǎn)

代理模式的主要缺點(diǎn)如下:

(1) 由于在客戶端和真實(shí)主題之間增加了代理對(duì)象萧恕,因此有些類型的代理模式可能會(huì)造成請(qǐng)求的處理速度變慢刚梭,例如保護(hù)代理。

(2) 實(shí)現(xiàn)代理模式需要額外的工作廊鸥,而且有些代理模式的實(shí)現(xiàn)過(guò)程較為復(fù)雜望浩,例如遠(yuǎn)程代理。

15.7.3 模式適用場(chǎng)景

代理模式的類型較多惰说,不同類型的代理模式有不同的優(yōu)缺點(diǎn)磨德,它們應(yīng)用于不同的場(chǎng)合:

(1) 當(dāng)客戶端對(duì)象需要訪問(wèn)遠(yuǎn)程主機(jī)中的對(duì)象時(shí)可以使用遠(yuǎn)程代理。

(2) 當(dāng)需要用一個(gè)消耗資源較少的對(duì)象來(lái)代表一個(gè)消耗資源較多的對(duì)象吆视,從而降低系統(tǒng)開(kāi)銷典挑、縮短運(yùn)行時(shí)間時(shí)可以使用虛擬代理,例如一個(gè)對(duì)象需要很長(zhǎng)時(shí)間才能完成加載時(shí)啦吧。

(3) 當(dāng)需要為某一個(gè)被頻繁訪問(wèn)的操作結(jié)果提供一個(gè)臨時(shí)存儲(chǔ)空間您觉,以供多個(gè)客戶端共享訪問(wèn)這些結(jié)果時(shí)可以使用緩沖代理。通過(guò)使用緩沖代理授滓,系統(tǒng)無(wú)須在客戶端每一次訪問(wèn)時(shí)都重新執(zhí)行操作琳水,只需直接從臨時(shí)緩沖區(qū)獲取操作結(jié)果即可。

(4) 當(dāng)需要控制對(duì)一個(gè)對(duì)象的訪問(wèn)般堆,為不同用戶提供不同級(jí)別的訪問(wèn)權(quán)限時(shí)可以使用保護(hù)代理在孝。

(5) 當(dāng)需要為一個(gè)對(duì)象的訪問(wèn)(引用)提供一些額外的操作時(shí)可以使用智能引用代理。

練習(xí)
1 Windows操作系統(tǒng)中的應(yīng)用程序快捷方式是( )模式的應(yīng)用實(shí)例淮摔。
A. 代理 (Proxy) B. 組合 (Composite)
C. 裝飾 (Decorator) D. 外觀 (Facade)

答:A (快捷方式做代理私沮,客戶無(wú)需關(guān)系具體實(shí)現(xiàn))

2 畢業(yè)生通過(guò)職業(yè)介紹所找工作,請(qǐng)問(wèn)該過(guò)程蘊(yùn)含了哪種設(shè)計(jì)模式和橙,繪制相應(yīng)的類圖仔燕。

答:代理模式。職業(yè)介紹所作為企業(yè)的代理魔招。


畢業(yè)生通過(guò)職業(yè)介紹所找工作類圖

3 在某應(yīng)用軟件中需要記錄業(yè)務(wù)方法的調(diào)用日志晰搀,在不修改現(xiàn)有業(yè)務(wù)類的基礎(chǔ)上為每一個(gè)類提供一個(gè)日志記錄代理類,在代理類中輸出日志办斑,如在業(yè)務(wù)方法Method()調(diào)用之前輸出“方法Method()被調(diào)用厕隧,調(diào)用時(shí)間為2012-11-5 10:10:10”,調(diào)用之后如果沒(méi)有拋異常則輸出“方法Method()調(diào)用成功”俄周,否則輸出“方法Method()調(diào)用失敗”吁讨。在代理類中調(diào)用真實(shí)業(yè)務(wù)類的業(yè)務(wù)方法,使用代理模式設(shè)計(jì)該日志記錄模塊的結(jié)構(gòu)峦朗,繪制類圖并使用Java語(yǔ)言編程模擬實(shí)現(xiàn)建丧。

答:


日志記錄模塊的結(jié)構(gòu)

4 某軟件公司欲開(kāi)發(fā)一款基于C/S的網(wǎng)絡(luò)圖片查看器,具體功能描述如下:用戶只需在圖片查看器中輸入網(wǎng)頁(yè)URL波势,程序?qū)⒆詣?dòng)將該網(wǎng)頁(yè)所有圖片下載到本地翎朱,考慮到有些網(wǎng)頁(yè)圖片比較多,而且某些圖片文件比較大尺铣,因此將先以圖標(biāo)的方式顯示圖片拴曲,不同類型的圖片使用不同的圖標(biāo),并且在圖標(biāo)下面標(biāo)注該圖片的文件名凛忿,用戶單擊圖標(biāo)后可查看真正的圖片澈灼,界面效果如圖15-7所示。試使用虛擬代理模式設(shè)計(jì)并實(shí)現(xiàn)該圖片查看器店溢。(注:可以結(jié)合多線程機(jī)制叁熔,使用一個(gè)線程顯示小圖標(biāo),同時(shí)啟動(dòng)另一個(gè)線程在后臺(tái)加載原圖床牧。)

圖15-7 圖片查看器界面效果圖

【友情提示:建議大家有時(shí)間的話把這些練習(xí)都做一做荣回,有問(wèn)題歡迎討論!】

練習(xí)會(huì)在我的github上做掉

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末戈咳,一起剝皮案震驚了整個(gè)濱河市心软,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌著蛙,老刑警劉巖删铃,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異册踩,居然都是意外死亡泳姐,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門(mén)暂吉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)胖秒,“玉大人,你說(shuō)我怎么就攤上這事慕的⊙指危” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵肮街,是天一觀的道長(zhǎng)风题。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么沛硅? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任眼刃,我火速辦了婚禮,結(jié)果婚禮上摇肌,老公的妹妹穿的比我還像新娘擂红。我一直安慰自己,他們只是感情好围小,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布昵骤。 她就那樣靜靜地躺著,像睡著了一般肯适。 火紅的嫁衣襯著肌膚如雪变秦。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,166評(píng)論 1 284
  • 那天框舔,我揣著相機(jī)與錄音蹦玫,去河邊找鬼。 笑死雨饺,一個(gè)胖子當(dāng)著我的面吹牛钳垮,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播额港,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼饺窿,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了移斩?” 一聲冷哼從身側(cè)響起肚医,我...
    開(kāi)封第一講書(shū)人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎向瓷,沒(méi)想到半個(gè)月后肠套,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡猖任,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年你稚,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片朱躺。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡刁赖,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出长搀,到底是詐尸還是另有隱情宇弛,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布源请,位于F島的核電站枪芒,受9級(jí)特大地震影響彻况,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜舅踪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一纽甘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧硫朦,春花似錦贷腕、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)瞒斩。三九已至破婆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間胸囱,已是汗流浹背祷舀。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留烹笔,地道東北人裳扯。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像谤职,于是被迫代替她去往敵國(guó)和親饰豺。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容

  • 1 場(chǎng)景問(wèn)題# 1.1 訪問(wèn)多條數(shù)據(jù)## 考慮這樣一個(gè)實(shí)際應(yīng)用:要一次性訪問(wèn)多條數(shù)據(jù)允蜈。 這個(gè)功能的背景是這樣的冤吨;在...
    七寸知架構(gòu)閱讀 2,974評(píng)論 1 52
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)饶套,斷路器漩蟆,智...
    卡卡羅2017閱讀 134,601評(píng)論 18 139
  • 目錄 本文的結(jié)構(gòu)如下: 引言 什么是代理模式 模式的結(jié)構(gòu) 典型代碼 代理模式分類 代碼示例 代理模式和裝飾者模式的...
    w1992wishes閱讀 1,517評(píng)論 0 13
  • 每日必讀:8條人生守則 1. 早睡早起 ——晚不超過(guò)22:30,早不晚于6:30妓蛮,醒后不睡回籠覺(jué)怠李,午休40分鐘 ...
    青銅女孩閱讀 209評(píng)論 2 0
  • Given a root node reference of a BST and a key, delete th...
    matrxyz閱讀 385評(píng)論 0 0