設(shè)計(jì)模式系列之代理模式
今天下班,同事們無(wú)聊中又聊到了食堂(怎么寫(xiě)食堂?吃貨啊?。?,辦公區(qū)離食堂遠(yuǎn)的同事老阮在大家你一句我一句的誘惑下,終于忍不住了決定不管在遠(yuǎn)也要找時(shí)間去嘗嘗,但是因?yàn)樯习嗖豁樎芬膊皇呛芊奖?,就委托我們宿舍的老李同志幫忙第二天先去開(kāi)卡充值,熱心腸的老李當(dāng)然不會(huì)拒絕嘍。
1、模擬場(chǎng)景
有了這個(gè)前奏今天的主題就當(dāng)然又有了,那么首先我們用代碼來(lái)實(shí)現(xiàn)上面的功能,首先來(lái)分析一下上面的場(chǎng)景:
①我們需要對(duì)卡片充值,所以需要一個(gè)卡片類,代碼如下:
- /// <summary> /// 卡片類
 - /// </summary>
 - public class Card
 - {
 - //卡片名稱
 - private string cardName = string.Empty;
 - public string CardName
 - {
 - get
 - {
 - return cardName;
 - }
 - set
 - {
 - cardName = value;
 - }
 - }
 - //構(gòu)造方法
 - public Card() { }
 - public Card(string cName)
 - {
 - cardName = cName;
 - }
 - }
 
?、诮酉聛?lái)就是路遠(yuǎn)的老阮,他的工卡需要去激活,代碼如下:
- /// <summary> /// 老阮
 - /// </summary>
 - public class MrRuan
 - {
 - //他有一個(gè)工卡
 - private Card myCard = new Card("mrRuan");
 - public Card MyCard
 - {
 - get
 - {
 - return myCard;
 - }
 - }
 - }
 
?、劢酉聛?lái)就是激活卡片當(dāng)然要去柜臺(tái)了,記得在單例模式中已經(jīng)將這個(gè)柜臺(tái)類實(shí)現(xiàn)了,那么我們就拿來(lái)用現(xiàn)成的吧,代碼如下:
- /// <summary> /// 柜臺(tái)類
 - /// </summary>
 - public sealed class Counter
 - {
 - //在第一次調(diào)用類成員時(shí),初始化唯一實(shí)例
 - private static readonly Counter instance = new Counter();
 - private Counter()
 - {
 - }
 - //返回類型實(shí)例屬性
 - public static Counter Instance
 - {
 - get
 - {
 - return instance;
 - }
 - }
 - //激活工卡方法
 - public void ActivationCard(Card card)
 - {
 - //激活工卡的過(guò)程
 - Console.WriteLine("{0}的工卡激活成功!",card.CardName);
 - }
 - }
 
?、茉倬褪菐椭宪浀诙烊ゼせ羁ㄆ睦侠?,因?yàn)槔侠钚枰ゼせ羁ㄆ?,所以包含激活卡片的操作,代碼如下:
- /// <summary> /// 熱心腸的老李
 - /// </summary>
 - public class MrLi
 - {
 - //激活工卡方法
 - public void ActivationCard(Card card)
 - {
 - Counter.Instance.ActivationCard(card);
 - }
 - }
 
?、蓊愋投汲橄蟪鰜?lái)之后當(dāng)然就是第二天去激活卡片啦,主函數(shù)調(diào)用代碼如下:
- static void Main(string[] args)
 - {
 - //實(shí)例化老阮
 - MrRuan ruan = new MrRuan();
 - //實(shí)例化老李
 - MrLi li = new MrLi();
 - //將卡片給老李,老李負(fù)責(zé)去激活
 - li.ActivationCard(ruan.MyCard);
 - }
 
仔細(xì)想了一下,這樣一來(lái)老李也就成了柜臺(tái)開(kāi)卡的代理人了,以后別人也可以委托他去幫忙激活卡片了,這不就是活生生的代理模式嗎?
這時(shí)你要問(wèn)了,那代理模式具體的定義是什么呀?到底什么是代理模式下?別著急繼續(xù)往下看吧。
2、代理模式
代理(Proxy)模式定義:為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問(wèn)。 類圖如下:

這時(shí)我們發(fā)現(xiàn)了多了一個(gè)接口ICounter,那么實(shí)現(xiàn)了接口的代理類與具體類的代碼如下:
- /// <summary> /// 柜臺(tái)類也就是具體類提供的功能規(guī)則
 - /// </summary>
 - public interface ICounter
 - {
 - void ActivationCard(Card card);
 - }
 - /// <summary> /// 熱心腸的老李
 - /// </summary>
 - public class MrLi : ICounter
 - {
 - //激活工卡方法
 - public void ActivationCard(Card card)
 - {
 - Counter.Instance.ActivationCard(card);
 - }
 - }
 - /// <summary> /// 柜臺(tái)類
 - /// </summary>
 - public sealed class Counter : ICounter
 - {
 - //在第一次調(diào)用類成員時(shí),初始化唯一實(shí)例
 - private static readonly Counter instance = new Counter();
 - private Counter()
 - {
 - }
 - //返回類型實(shí)例屬性
 - public static Counter Instance
 - {
 - get
 - {
 - return instance;
 - }
 - }
 - //激活工卡方法
 - public void ActivationCard(Card card)
 - {
 - //激活工卡的過(guò)程
 - Console.WriteLine("{0}的工卡激活成功!",card.CardName);
 - }
 - }
 
那么這個(gè)接口有什么用呢?
在面向?qū)ο笤O(shè)計(jì)中,對(duì)象之間需要進(jìn)行交互和通信。例如:上面的代理類MrLi調(diào)用了具體類柜臺(tái)類counter的激活卡片的方法(ActiviationCard),那么這個(gè)時(shí)候代理類MrLi不在代理counter柜臺(tái)的激活卡片功能了,而是去另一個(gè)counterNew的柜臺(tái)去激活,但是counterNew柜臺(tái)激活卡片的方法是(CounterActiviationCard),怎么辦?我們需要去修改調(diào)用counter的類,那么如何降低耦合性呢?當(dāng)然就是將接口和實(shí)現(xiàn)分離開(kāi)來(lái),這樣組件間和日志對(duì)象之間的依賴就是基于接口,而不是實(shí)現(xiàn)!
例如:目前MrLi與counter之間的調(diào)用如下:
- /// <summary> /// 熱心腸的老李
 - /// </summary>
 - public class MrLi
 - {
 - //激活工卡方法
 - public void ActivationCard(Card card)
 - {
 - Counter.Instance.ActivationCard(card);
 - }
 - }
 - 那么如果需要加入新的柜臺(tái)類(counterNew),它的激活方法叫(CounterActivationCard),代碼如下:
 - /// <summary> /// 新的柜臺(tái)類
 - /// </summary>
 - public sealed class NewCounter
 - {
 - //在第一次調(diào)用類成員時(shí),初始化唯一實(shí)例
 - private static readonly NewCounter instance = new NewCounter();
 - private NewCounter()
 - {
 - }
 - //返回類型實(shí)例屬性
 - public static NewCounter Instance
 - {
 - get
 - {
 - return instance;
 - }
 - }
 - //激活工卡方法
 - public void CounterActivationCard(Card card)
 - {
 - //激活工卡的過(guò)程
 - Console.WriteLine("{0}的工卡激活成功!", card.CardName);
 - }
 - }
 
這樣兩個(gè)類就緊密的耦合在一起了,若Counter類改變,那么MrLi類也不得不改變,這時(shí)我們?nèi)绻胧褂眯碌墓衽_(tái)類(NewCounter),也需要修改調(diào)用者本身。
所以我們需要使用接口分離實(shí)現(xiàn)。代碼如下:
- /// <summary> /// 柜臺(tái)類也就是具體類提供的功能規(guī)則
 - /// </summary>
 - public interface ICounter
 - {
 - void ActivationCard(Card card);
 - }
 - /// <summary> /// 熱心腸的老李
 - /// </summary>
 - public class MrLi : ICounter
 - {
 - //激活工卡方法
 - public void ActivationCard(Card card)
 - {
 - Counter.Instance.ActivationCard(card);
 - }
 - }
 - /// <summary> /// 柜臺(tái)類
 - /// </summary>
 - public sealed class Counter : ICounter
 - {
 - //在第一次調(diào)用類成員時(shí),初始化唯一實(shí)例
 - private static readonly Counter instance = new Counter();
 - private Counter()
 - {
 - }
 - //返回類型實(shí)例屬性
 - public static Counter Instance
 - {
 - get
 - {
 - return instance;
 - }
 - }
 - //激活工卡方法
 - public void ActivationCard(Card card)
 - {
 - //激活工卡的過(guò)程
 - Console.WriteLine("{0}的工卡激活成功!",card.CardName);
 - }
 - }
 - /// <summary> /// 新的柜臺(tái)類
 - /// </summary>
 - public sealed class NewCounter : ICounter
 - {
 - //在第一次調(diào)用類成員時(shí),初始化唯一實(shí)例
 - private static readonly NewCounter instance = new NewCounter();
 - private NewCounter()
 - {
 - }
 - //返回類型實(shí)例屬性
 - public static NewCounter Instance
 - {
 - get
 - {
 - return instance;
 - }
 - }
 - //激活工卡方法
 - public void ActivationCard(Card card)
 - {
 - //激活工卡的過(guò)程
 - Console.WriteLine("{0}的工卡激活成功!", card.CardName);
 - }
 - }
 
有了接口的約束,所有柜臺(tái)類都遵循了這個(gè)約束將激活卡片的方法統(tǒng)一成(ActivationCard),那么在將來(lái)切換對(duì)象的時(shí)候都可以以一種統(tǒng)一的調(diào)用方式去無(wú)縫的卻換。
這時(shí)細(xì)心的朋友還會(huì)說(shuō),Mr.Li的ActivationCard方法中調(diào)用的是具體的counter類型如果換成Newcounter,還是要去修改它的代碼,你現(xiàn)在只能是不用去修改調(diào)用方法了而已,想的好,還記得我們當(dāng)時(shí)的工廠模式嗎?它的好處不就是降低耦合嗎?為什么不用?
那么加入工廠類(CounterProvider),在修改一下MrLi的調(diào)用使它的調(diào)用依賴于抽象接口而不是具體的實(shí)現(xiàn),代碼如下:
- /// <summary> /// 熱心腸的老李
 - /// </summary>
 - public class MrLi : ICounter
 - {
 - //激活工卡方法
 - public void ActivationCard(Card card)
 - {
 - ICounter counter = CounterProvider.GetCounter();
 - counter.ActivationCard(card);
 - }
 - }
 - /// <summary> /// 柜臺(tái)類工廠
 - /// </summary>
 - public class CounterProvider
 - {
 - public static ICounter GetCounter()
 - {
 - ICounter counter = null
 - //從配置文件確定實(shí)例化那個(gè)柜臺(tái)類
 - if (ReadConfig)
 - {
 - counter = Counter.Instance;
 - }
 - else
 - {
 - counter = NewCounter.Instance;
 - }
 - }
 - }
 
這樣我們就徹底的實(shí)現(xiàn)了代理模式,并且詮釋了如何使用接口的好處和靈活組合模式與靈活理解模式與使用。
3、應(yīng)用場(chǎng)景
那么代理模式的幾種使用場(chǎng)景我們來(lái)看一看:
?、龠h(yuǎn)程代理:為一個(gè)對(duì)象在不同地址空間提供局部代表。這樣可以隱藏一個(gè)對(duì)象存在于不同地址空間的事實(shí),例如:老阮(MrRuan)在地點(diǎn)A,老李在地點(diǎn)B,餐廳柜臺(tái)也在地點(diǎn)B,那么老李和老軟住在一起(都在地點(diǎn)A?。?,那么老李就是餐廳(地點(diǎn)B)在老軟與老李住處(地點(diǎn)A)的代表。
?、谔摂M代理:是根據(jù)需要?jiǎng)?chuàng)建開(kāi)銷很大的對(duì)象。通過(guò)它來(lái)存放實(shí)例化需要很長(zhǎng)時(shí)間的真是對(duì)象,例如:老阮(MrRuan)在地點(diǎn)A,到餐廳柜臺(tái)(地點(diǎn)B),因?yàn)榫嚯x遠(yuǎn)卻是很費(fèi)勁,而老李剛好在這里(地點(diǎn)B)上班,所以讓老李去辦是很可行的辦法。(不太恰當(dāng))
③安全代理:用來(lái)控制真是對(duì)象訪問(wèn)時(shí)的權(quán)限,例如:老阮跟餐廳的柜臺(tái)MM剛分手不方便去辦理,所以需要借助老李去完成事項(xiàng)的辦理。
?、苤悄艽恚?strong>是指當(dāng)調(diào)用真是的對(duì)象時(shí),代理去處理另外一些事情,例如:老李幫助老阮辦理卡片激活時(shí),順便說(shuō)說(shuō)老阮的好話,讓她倆能夠和好。
原文鏈接:http://www.cnblogs.com/doubleliang/archive/2012/01/12/2321092.html
【編輯推薦】















 
 
 







 
 
 
 