C++設(shè)計(jì)模式之代理模式
前言
青春總是那樣,逝去了才開(kāi)始回味;大學(xué)生活也是在不經(jīng)意間就溜走了,現(xiàn)在上班的時(shí)候,偶爾還會(huì)懷念大學(xué)時(shí),大家在一起玩游戲的時(shí)光。大學(xué)喜歡玩游戲,但是可悲的校園網(wǎng),速度能把人逼瘋了;還好,后來(lái)搞了一個(gè)游戲代理,總算能勉勉強(qiáng)強(qiáng)的玩了兩年。時(shí)至今日,敲起鍵盤(pán)寫(xiě)設(shè)計(jì)模式的時(shí)候,又想起了那些美好的時(shí)光。好了,這是一篇技術(shù)文章,而不是抒情懷舊的散文;思緒再回到這篇文章上來(lái),游戲代理,是個(gè)什么東西,有了它就能讓我們玩游戲的延遲立馬下來(lái)了。今天,我并不會(huì)去總結(jié)游戲代理是如何實(shí)現(xiàn)的,重點(diǎn)是通過(guò)游戲代理這個(gè)例子來(lái)總結(jié)設(shè)計(jì)模式中的代理模式。
什么是代理模式?
在GOF的《設(shè)計(jì)模式:可復(fù)用面向?qū)ο筌浖幕A(chǔ)》一書(shū)中對(duì)代理模式是這樣說(shuō)的:為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問(wèn)。結(jié)合上面的游戲代理的例子和下面的圖,我們來(lái)進(jìn)行分析一下。以前你是這樣玩游戲:
現(xiàn)在有了游戲代理,你是這樣玩游戲:
代理服務(wù)器干了什么?它代替你去和游戲服務(wù)器進(jìn)行交互。它訪問(wèn)游戲服務(wù)器的速度比你使用校園網(wǎng)訪問(wèn)游戲服務(wù)器的速度快很多。所以,你的游戲延遲就下來(lái)了。
代理模式分為四類(lèi):遠(yuǎn)程代理,虛代理,保護(hù)代理和智能引用。在下面使用場(chǎng)合會(huì)對(duì)這四種進(jìn)行分別介紹。
UML類(lèi)圖
Proxy
1.保存一個(gè)引用使得代理可以訪問(wèn)實(shí)體。若RealSubject和Subject的接口相同,Proxy會(huì)引用Subject,就相當(dāng)于在代理類(lèi)中保存一個(gè)Subject指針,該指針會(huì)指向RealSubject;
2.提供一個(gè)與Subject的接口相同的接口,這樣代理就可以用來(lái)替代實(shí)體;
3.控制對(duì)實(shí)體的存取,并可能負(fù)責(zé)創(chuàng)建和刪除它;
4.其它功能依賴(lài)于代理的類(lèi)型,例如:
遠(yuǎn)程代理負(fù)責(zé)對(duì)請(qǐng)求及其參數(shù)進(jìn)行編碼,并向不同地址空間中的實(shí)體發(fā)送已編碼的請(qǐng)求;
虛代理可以緩存實(shí)體的附加信息,以便延遲對(duì)它的訪問(wèn);
保護(hù)代理檢查調(diào)用者是否具有實(shí)現(xiàn)一個(gè)請(qǐng)求所必須的訪問(wèn)權(quán)限。
Subject:定義RealSubject和Proxy的共用接口,這樣就在任何使用RealSubject的地方都可以使用Proxy;
RealSubject:定義Proxy所代理的實(shí)體。
使用場(chǎng)合
上面也總結(jié)了,代理模式分為遠(yuǎn)程代理,虛代理,保護(hù)代理和智能引用這四種,而分為這四種,就是對(duì)應(yīng)不同的使用場(chǎng)合的。
1.遠(yuǎn)程代理為一個(gè)對(duì)象在不同的地址空間提供局部代理;
2.虛代理根據(jù)需求創(chuàng)建開(kāi)銷(xiāo)很大的對(duì)象;
3.保護(hù)代理控制原始對(duì)象的訪問(wèn);保護(hù)代理用于對(duì)象應(yīng)該有不同的訪問(wèn)權(quán)限的時(shí)候;
4.智能引用取代了簡(jiǎn)單的指針,它在訪問(wèn)對(duì)象時(shí)執(zhí)行一些附加操作,它的典型用途包括:
對(duì)指向?qū)嶋H對(duì)象的引用計(jì)數(shù),這樣當(dāng)該對(duì)象沒(méi)有引用時(shí),可以自動(dòng)釋放它;
當(dāng)?shù)谝淮我靡粋€(gè)持久對(duì)象時(shí),將它裝入內(nèi)存;
在訪問(wèn)一個(gè)實(shí)際對(duì)象前,檢查是否已經(jīng)鎖定了它,以確保其他對(duì)象不能改變它。
代碼實(shí)現(xiàn)
最簡(jiǎn)單的實(shí)現(xiàn),對(duì)上述UML類(lèi)圖的直接代碼體現(xiàn):
#include <iostream>
using namespace std;
#define SAFE_DELETE(p) if (p) { delete p; p = NULL;}
class CSubject
{
public:
CSubject(){};
virtual ~CSubject(){}
virtual void Request() = 0;
};
class CRealSubject : public CSubject
{
public:
CRealSubject(){}
~CRealSubject(){}
void Request()
{
cout<<"CRealSubject Request"<<endl;
}
};
class CProxy : public CSubject
{
public:
CProxy() : m_pRealSubject(NULL){}
~CProxy()
{
SAFE_DELETE(m_pRealSubject);
}
void Request()
{
if (NULL == m_pRealSubject)
{
m_pRealSubject = new CRealSubject();
}
cout<<"CProxy Request"<<endl;
m_pRealSubject->Request();
}
private:
CRealSubject *m_pRealSubject;
};
int main()
{
CSubject *pSubject = new CProxy();
pSubject->Request();
SAFE_DELETE(pSubject);
}
上面的實(shí)現(xiàn),就是對(duì)代理模式的最原始體現(xiàn),現(xiàn)在提供一個(gè)有血有肉的實(shí)際應(yīng)用級(jí)的體現(xiàn):
#include <iostream>
#include <windows.h>
using namespace std;
#define SAFE_DELETE(p) if (p) { delete p; p = NULL; }
class KRefCount
{
public:
KRefCount():m_nCount(0){}
public:
unsigned AddRef(){ return InterlockedIncrement(&m_nCount); }
unsigned Release(){ return InterlockedDecrement(&m_nCount); }
void Reset(){ m_nCount = 0; }
private:
unsigned long m_nCount;
};
template <typename T>
class SmartPtr
{
public:
SmartPtr(void)
: m_pData(NULL)
{
m_pReference = new KRefCount();
m_pReference->AddRef();
}
SmartPtr(T* pValue)
: m_pData(pValue)
{
m_pReference = new KRefCount();
m_pReference->AddRef();
}
SmartPtr(const SmartPtr<T>& sp)
: m_pData(sp.m_pData)
, m_pReference(sp.m_pReference)
{
m_pReference->AddRef();
}
~SmartPtr(void)
{
if (m_pReference && m_pReference->Release() == 0)
{
SAFE_DELETE(m_pData);
SAFE_DELETE(m_pReference);
}
}
inline T& operator*()
{
return *m_pData;
}
inline T* operator->()
{
return m_pData;
}
SmartPtr<T>& operator=(const SmartPtr<T>& sp)
{
if (this != &sp)
{
if (m_pReference && m_pReference->Release() == 0)
{
SAFE_DELETE(m_pData);
SAFE_DELETE(m_pReference);
}
m_pData = sp.m_pData;
m_pReference = sp.m_pReference;
m_pReference->AddRef();
}
return *this;
}
SmartPtr<T>& operator=(T* pValue)
{
if (m_pReference && m_pReference->Release() == 0)
{
SAFE_DELETE(m_pData);
SAFE_DELETE(m_pReference);
}
m_pData = pValue;
m_pReference = new KRefCount;
m_pReference->AddRef();
return *this;
}
T* Get()
{
T* ptr = NULL;
ptr = m_pData;
return ptr;
}
void Attach(T* pObject)
{
if (m_pReference->Release() == 0)
{
SAFE_DELETE(m_pData);
SAFE_DELETE(m_pReference);
}
m_pData = pObject;
m_pReference = new KRefCount;
m_pReference->AddRef();
}
T* Detach()
{
T* ptr = NULL;
if (m_pData)
{
ptr = m_pData;
m_pData = NULL;
m_pReference->Reset();
}
return ptr;
}
private:
KRefCount* m_pReference;
T* m_pData;
};
class CTest
{
public:
CTest(int b) : a(b) {}
private:
int a;
};
int main()
{
SmartPtr<CTest> pSmartPtr1(new CTest(10));
SmartPtr<CTest> pSmartPtr2(new CTest(20));
pSmartPtr1 = pSmartPtr2;
}
智能指針使用引用計(jì)數(shù)實(shí)現(xiàn)時(shí),就是最好的使用代理模式的例子。在上面的例子中,SmartPtr就是一個(gè)代理類(lèi),而T* m_pData才是實(shí)際的數(shù)據(jù)。SmartPtr代理實(shí)際的數(shù)據(jù),去實(shí)現(xiàn)了指針的行為,添加了引用計(jì)數(shù),從而實(shí)現(xiàn)了智能指針。
總結(jié)
我在第一次接觸代理模式的時(shí)候,看它的UML類(lèi)圖,發(fā)現(xiàn)它和適配器模式的類(lèi)適配器很像,再一看,又和裝飾模式非常像;不仔細(xì)區(qū)分,真的是很容易混亂的。下面就做簡(jiǎn)單的區(qū)分,說(shuō)多了也都是“狡辯”了。
1.適配器Adapter為它所適配的對(duì)象提供了一個(gè)不同的接口。相反,代理提供了與它的實(shí)體相同的接口。然而,用于訪問(wèn)保護(hù)的代理可能會(huì)拒絕執(zhí)行實(shí)體會(huì)執(zhí)行的操作,因此,它的接口實(shí)際上可能只是實(shí)體接口的一個(gè)子集。
2.盡管Decorator的實(shí)現(xiàn)部分與代理相似,但Decorator的目的不一樣。Decorator為對(duì)象添加一個(gè)或多個(gè)功能,而代理則控制對(duì)對(duì)象的訪問(wèn)。
我在這里進(jìn)行區(qū)分,你們看了,也就像我在咬文嚼字一樣;這就是結(jié)構(gòu)型設(shè)計(jì)模式;它們之間都有一些細(xì)微的差別。你也可以說(shuō),在適配器模式進(jìn)行接口適配時(shí),添加一些數(shù)據(jù)轉(zhuǎn)換就變成了遠(yuǎn)程代理;你也可以說(shuō)裝飾模式雖然功能不一樣,在我看來(lái),大同小異;是的,不管你怎么說(shuō),就像1000個(gè)讀者心中有1000個(gè)哈姆雷特一樣,每個(gè)人對(duì)設(shè)計(jì)模式的理解都是不一樣的;最重要的是我們能在實(shí)際應(yīng)用中進(jìn)行活學(xué)活用,如果能做到這個(gè);不管什么設(shè)計(jì)模式,那只是一個(gè)名字,就像對(duì)于富人來(lái)說(shuō),錢(qián)只是一個(gè)銀行卡上的一個(gè)數(shù)字一樣。
最后,我堅(jiān)信分享使我們更加進(jìn)步,期待大家和我分享你的設(shè)計(jì)模式心得。
相關(guān)文章
C語(yǔ)言實(shí)現(xiàn)樹(shù)的動(dòng)態(tài)查找實(shí)例代碼
這篇文章主要介紹了C語(yǔ)言實(shí)現(xiàn)樹(shù)的動(dòng)態(tài)查找實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2017-06-06C++實(shí)現(xiàn)圖書(shū)管理系統(tǒng)課程設(shè)計(jì)(面向?qū)ο?
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)圖書(shū)管理系統(tǒng)課程設(shè)計(jì),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03vscode 配置 C/C++ 編譯環(huán)境的詳細(xì)圖文教程
這篇文章主要介紹了vscode 配置 C/C++ 編譯環(huán)境的詳細(xì)教程,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-05-05C語(yǔ)言實(shí)現(xiàn)文件操作實(shí)例(簡(jiǎn)單圖示講解)
與普通文件載體不同,文件是以硬盤(pán)為載體存儲(chǔ)在計(jì)算機(jī)上的信息集合,文件可以是文本文檔、圖片、程序等等,下面這篇文章主要給大家介紹了關(guān)于C語(yǔ)言實(shí)現(xiàn)文件操作實(shí)例的相關(guān)資料,需要的朋友可以參考下2023-02-02C語(yǔ)言基礎(chǔ) 原碼、反碼、補(bǔ)碼和移碼詳解
這篇文章主要介紹了C語(yǔ)言基礎(chǔ) 原碼、反碼、補(bǔ)碼和移碼詳解的相關(guān)資料,需要的朋友可以參考下2017-01-01教你如何使用qt quick-PathView實(shí)現(xiàn)好看的home界面
pathView的使用類(lèi)似與ListView,都需要模型(model)和代理(delegate),只不過(guò)pathView多了一個(gè)路徑(path)屬性,顧名思義路徑就是item滑動(dòng)的路徑,下面給大家分享qt quick-PathView實(shí)現(xiàn)好看的home界面,一起看看吧2021-06-06C++ 中時(shí)間與時(shí)間戳的轉(zhuǎn)換實(shí)例詳解
這篇文章主要介紹了C++ 中時(shí)間與時(shí)間戳的轉(zhuǎn)換實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-06-06C++基本用法實(shí)踐之移動(dòng)語(yǔ)義詳解
移動(dòng)(move)語(yǔ)義是C++引入了一種新的內(nèi)存優(yōu)化,以避免不必要的拷貝,下面小編就來(lái)和大家簡(jiǎn)單聊聊C++中移動(dòng)語(yǔ)義的相關(guān)使用吧,希望對(duì)大家有所幫助2023-07-07