程式設計模式(十二) C++ 代理(Proxy)模式
2.7 Proxy
代理模式為其他物件提供一種代理以控制對這個物件的訪問。 在需要用比較通用和複雜的物件指標代替簡單的的指標的時候,使用代理模式。有四種常用的情況:
1、遠端代理,也就是為一個物件在不同的地址空間提供區域性代表。這樣可以隱藏一個物件存在於不同地址空間的事實。
2、虛擬代理,是根據需要建立開銷很大的物件。通過它來存放例項化需要很長時間的真實物件。
3、安全代理,用來控制真實物件訪問的許可權。
4、智慧指針,取代了簡單的指標,它在訪問物件時執行一些附加操作。
參與者:
Proxy
— 儲存一個引用使得代理可以訪問實體。若RealSubject和Subject的介面相同,proxy會引用Subject。
— 提供一個與Subject的介面相同的介面,這樣代理就可以用來代替實體。
— 控制對實體的存取,並可能負責建立和刪除它。
— 其它功能依賴於代理的型別。
Subject
— 定義RealSubject和Proxy的共用介面,這樣就可以在任何使用RealSubject的地方都可以使用Proxy。
RealSubject
— 定義Proxy所代表的實體。
思想:作為C++工程師,免不了要管理記憶體,記憶體管理也是C++中的難點,而智慧指標採用引用計數的辦法很方便的幫我們管理了內
存的使用,極大方便了我們的工作效率。而智慧指標的這種用法其實就是代理模式的一種,他幫我們控制了該物件的記憶體使用。代理
模式就是為其他物件提供一種代理來控制對這個物件的訪問。
場景:需要注意體會他和Decorator的需別。Proxy是繼承需要修飾的類,而Decorator用的是包含的方式。Proxy模式,或者準確地
說DynamicProxy模式,是現代AOP框架實現中的一種常用方式。典型的實現如Spring,JBoss以及CastleProject中的Aspect。
實現:繼承,並在過載方法中新增需要的修飾功能。
例項:
(1) 遠端代理
大話設計模式裡面的例子:小王想追求小娟,但他不認識小娟。但他的朋友小林認識小娟,所以他通過讓小林幫忙送禮物的方式追求小娟。
這裡的小林就是我們的代理!
首先,我們實現一個女孩類:
class Girl{ public: Girl(char* name = ""):mName(name){} char* getName() { return mName; } private: char* mName; };
送禮物的介面:
class GiveGift { public: virtual void GiveDolls() = 0; virtual void GiveFlowers() = 0; virtual void GiveChocolate() = 0; };
送禮物例項類(小王)
class Puisuit : public GiveGift { public: Puisuit(Girl mm):mGirl(mm){} virtual void GiveDolls() { cout<<"送"<<mGirl.getName()<<"玩具!"<<endl; } virtual void GiveFlowers() { cout<<"送"<<mGirl.getName()<<"鮮花!"<<endl; } virtual void GiveChocolate() { cout<<"送"<<mGirl.getName()<<"巧克力!"<<endl; } private: Girl mGirl; };
送禮物代理類(小林)
class Proxy : public GiveGift { public: Proxy(Girl mm) { mPuisuit = new Puisuit(mm); } virtual void GiveDolls() { mPuisuit->GiveDolls(); } virtual void GiveFlowers() { mPuisuit->GiveFlowers(); } virtual void GiveChocolate() { mPuisuit->GiveChocolate(); } private: Puisuit* mPuisuit; };
客戶端程式碼:
#include <iostream> #include "Proxy.h" using namespace std; int main() { Girl mm("小娟"); Proxy pro(mm); pro.GiveChocolate(); pro.GiveDolls(); pro.GiveFlowers(); return 0; }
(2)虛擬代理
考慮一個可以在文件中嵌入圖形物件的文件編輯器。有些圖形物件的建立開銷很大。但是開啟文件必須很迅速,因此我們在開啟文件時應避免一次性建立所有開銷
很大的物件。這裡就可以運用代理模式,在開啟文件時,並不開啟圖形物件,而是開啟圖形物件的代理以替代真實的圖形。待到真正需要開啟圖形時,仍由代理負
責開啟。
簡單實現如下:
- class Image
- {
- public:
- Image(string name): m_imageName(name) {}
- virtual ~Image() {}
- virtualvoid Show() {}
- protected:
- string m_imageName;
- };
- class BigImage: public Image
- {
- public:
- BigImage(string name):Image(name) {}
- ~BigImage() {}
- void Show() { cout<<"Show big image : "<<m_imageName<<endl; }
- };
- class BigImageProxy: public Image
- {
- private:
- BigImage *m_bigImage;
- public:
- BigImageProxy(string name):Image(name),m_bigImage(0) {}
- ~BigImageProxy() { delete m_bigImage; }
- void Show()
- {
- if(m_bigImage == NULL)
- m_bigImage = new BigImage(m_imageName);
- m_bigImage->Show();
- }
- };
客戶呼叫:
- int main()
- {
- Image *image = new BigImageProxy("proxy.jpg"); //代理
- image->Show(); //需要時由代理負責開啟
- delete image;
- return 0;
- }
在這個例子屬於虛代理的情況,下面給兩個智慧引用的例子。一個是C++中的auto_ptr,另一個是smart_ptr。自己實現了一下。先給出auto_ptr的程式碼實現
- template<class T>
- class auto_ptr {
- public:
- explicit auto_ptr(T *p = 0): pointee(p) {}
- auto_ptr(auto_ptr<T>& rhs): pointee(rhs.release()) {}
- ~auto_ptr() { delete pointee; }
- auto_ptr<T>& operator=(auto_ptr<T>& rhs)
- {
- if (this != &rhs) reset(rhs.release());
- return *this;
- }
- T& operator*() const { return *pointee; }
- T* operator->() const { return pointee; }
- T* get() const { return pointee; }
- T* release()
- {
- T *oldPointee = pointee;
- pointee = 0;
- return oldPointee;
- }
- void reset(T *p = 0)
- {
- if (pointee != p) {
- delete pointee;
- pointee = p;
- }
- }
- private:
- T *pointee;
- };
分配的物件提供異常安全。因為它用一個物件儲存需要被自動釋放的資源,然後依靠物件的解構函式來釋放資源。這樣客戶就不需要關注資源的釋放,由auto_ptr
物件自動完成。實現中的一個關鍵就是過載瞭解引用操作符和箭頭操作符,從而使得auto_ptr的使用與真實指標類似。
(3)安全代理
遊戲中,通過代理來控制不同vip玩家的遊戲許可權。
基本操作介面
class Play { public: virtual void Play1() = 0; virtual void Play2() = 0; virtual void Play3() = 0; };
操作類:
class Player:public Play { public: void Play1() { cout<<"戰役"<<endl; } void Play2() { cout<<"軍團"<<endl; } void Play3() { cout<<"神器"<<endl; } };
不同vip玩家的代理:
class ProxyPlayerVip0:Play { public: ProxyPlayerVip0() { mPlayer = new Player; } void Play1() { mPlayer->Play1(); } void Play2() { cout<<"沒有許可權"<<endl; } void Play3() { cout<<"沒有許可權"<<endl; } private: Play* mPlayer; }; class ProxyPlayerVip1:Play { public: ProxyPlayerVip1() { mPlayer = new Player; } void Play1() { mPlayer->Play1(); } void Play2() { mPlayer->Play2(); } void Play3() { cout<<"沒有許可權"<<endl; } private: Play* mPlayer; };
客戶端程式碼:
ProxyPlayerVip0 pro5;
pro5.Play1();
pro5.Play2();
pro5.Play3();
ProxyPlayerVip1 pro1;
pro1.Play1();
pro1.Play2();
pro1.Play3();
結果:
戰役
沒有許可權
沒有許可權
戰役
軍團
沒有許可權
(4)智慧指針
- #include "stdafx.h"
- #include <assert.h>
- #define KSAFE_DELETE(p) \
- if (p) \
- { \
- delete p; \
- p = NULL; \
- }
- class KRefCount
- {
- public:
- KRefCount():m_nCount(0){}
- public:
- void AddRef(){m_nCount++;}
- int Release(){return --m_nCount;}
- void Reset(){m_nCount=0;}
- private:
- int m_nCount;
- };
- template <typename T>
- class KSmartPtr
- {
- public:
- KSmartPtr(void)
- : m_pData(NULL)
- {
- m_pReference = new KRefCount();
- m_pReference->AddRef();
- }
- KSmartPtr(T* pValue)
- : m_pData(pValue)
- {
- m_pReference = new KRefCount();
- m_pReference->AddRef();
- }
- KSmartPtr(const KSmartPtr<T>& sp)
- : m_pData(sp.m_pData)
- , m_pReference(sp.m_pReference)
- {
- m_pReference->AddRef();
- }
- ~KSmartPtr(void)
- {
- if (m_pReference && m_pReference->Release() == 0)
- {
- KSAFE_DELETE(m_pData);
- KSAFE_DELETE(m_pReference);
- }
- }
- inline T& operator*()
- {
- return *m_pData;
- }
- inline T* operator->()
- {
- return m_pData;
- }
- KSmartPtr<T>& operator=(const KSmartPtr<T>& sp)
- {
- if (this != &sp)
- {
- if (m_pReference && m_pReference->Release() == 0)
- {
- KSAFE_DELETE(m_pData);
- KSAFE_DELETE(m_pReference);
- }
- m_pData = sp.m_pData;
- m_pReference = sp.m_pReference;
- m_pReference->AddRef();
- }
- return *this;
- }
- KSmartPtr<T>& operator=(T* pValue)
- {
- if (m_pReference && m_pReference->Release() == 0)
- {
- KSAFE_DELETE(m_pData);
- KSAFE_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)
- {
- KSAFE_DELETE(m_pData);
- KSAFE_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;
- }
重構成本:低。