【學(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所示:
在軟件開(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可知,代理模式包含如下三個(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中,客戶端對(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中,業(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ì)象交互即可源譬。
擴(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所示:
在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è)的代理魔招。
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)建丧。
答:
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)加載原圖床牧。)
【友情提示:建議大家有時(shí)間的話把這些練習(xí)都做一做荣回,有問(wèn)題歡迎討論!】
練習(xí)會(huì)在我的github上做掉