1. 程式人生 > >程式設計模式(十二) C++ 代理(Proxy)模式

程式設計模式(十二) 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框架實現中的一種常用方式。典型的實現如SpringJBoss以及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)虛擬代理

考慮一個可以在文件中嵌入圖形物件的文件編輯器。有些圖形物件的建立開銷很大。但是開啟文件必須很迅速,因此我們在開啟文件時應避免一次性建立所有開銷

很大的物件。這裡就可以運用代理模式,在開啟文件時,並不開啟圖形物件,而是開啟圖形物件的代理以替代真實的圖形。待到真正需要開啟圖形時,仍由代理負

責開啟。

簡單實現如下:

  1. class Image  
  2. {  
  3. public:  
  4.     Image(string name): m_imageName(name) {}  
  5.     virtual ~Image() {}  
  6.     virtualvoid Show() {}  
  7. protected:  
  8.     string m_imageName;  
  9. };  
  10. class BigImage: public Image  
  11. {  
  12. public:  
  13.     BigImage(string name):Image(name) {}  
  14.     ~BigImage() {}  
  15.     void Show() { cout<<"Show big image : "<<m_imageName<<endl; }  
  16. };  
  17. class BigImageProxy: public Image  
  18. {  
  19. private:  
  20.     BigImage *m_bigImage;  
  21. public:  
  22.     BigImageProxy(string name):Image(name),m_bigImage(0) {}  
  23.     ~BigImageProxy() { delete m_bigImage; }  
  24.     void Show()   
  25.     {  
  26.         if(m_bigImage == NULL)  
  27.             m_bigImage = new BigImage(m_imageName);  
  28.         m_bigImage->Show();  
  29.     }  
  30. };  

         客戶呼叫:

  1. int main()  
  2. {  
  3.     Image *image = new BigImageProxy("proxy.jpg"); //代理
  4.     image->Show(); //需要時由代理負責開啟
  5.     delete image;  
  6.     return 0;  
  7. }  

         在這個例子屬於虛代理的情況,下面給兩個智慧引用的例子。一個是C++中的auto_ptr,另一個是smart_ptr。自己實現了一下。先給出auto_ptr的程式碼實現

  1. template<class T>    
  2. class auto_ptr {    
  3. public:    
  4.     explicit auto_ptr(T *p = 0): pointee(p) {}    
  5.     auto_ptr(auto_ptr<T>& rhs): pointee(rhs.release()) {}    
  6.     ~auto_ptr() { delete pointee; }    
  7.     auto_ptr<T>& operator=(auto_ptr<T>& rhs)    
  8.     {    
  9.         if (this != &rhs) reset(rhs.release());    
  10.         return *this;    
  11.     }    
  12.     T& operator*() const { return *pointee; }    
  13.     T* operator->() const { return pointee; }    
  14.     T* get() const { return pointee; }    
  15.     T* release()    
  16.     {    
  17.         T *oldPointee = pointee;    
  18.         pointee = 0;    
  19.         return oldPointee;    
  20.     }    
  21.     void reset(T *p = 0)    
  22.     {    
  23.         if (pointee != p) {    
  24.                delete pointee;    
  25.                pointee = p;    
  26.             }    
  27.         }    
  28. private:    
  29.     T *pointee;    
  30. };   
閱讀上面的程式碼,我們可以發現 auto_ptr 類就是一個代理,客戶只需操作auto_prt的物件,而不需要與被代理的指標pointee打交道。auto_ptr 的好處在於為動態

分配的物件提供異常安全。因為它用一個物件儲存需要被自動釋放的資源,然後依靠物件的解構函式來釋放資源。這樣客戶就不需要關注資源的釋放,由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)智慧指

  1. #include "stdafx.h"
  2. #include <assert.h>
  3. #define KSAFE_DELETE(p) \
  4.     if (p)           \  
  5.         {                \  
  6.         delete p;    \  
  7.         p = NULL;    \  
  8.         }  
  9. class KRefCount  
  10. {  
  11. public:  
  12.     KRefCount():m_nCount(0){}  
  13. public:  
  14.     void AddRef(){m_nCount++;}  
  15.     int Release(){return --m_nCount;}  
  16.     void Reset(){m_nCount=0;}  
  17. private:  
  18.     int m_nCount;  
  19. };  
  20. template <typename T>  
  21. class KSmartPtr  
  22. {  
  23. public:  
  24.     KSmartPtr(void)  
  25.         : m_pData(NULL)  
  26.     {  
  27.         m_pReference = new KRefCount();  
  28.         m_pReference->AddRef();  
  29.     }  
  30.     KSmartPtr(T* pValue)  
  31.         : m_pData(pValue)  
  32.     {  
  33.         m_pReference = new KRefCount();  
  34.         m_pReference->AddRef();  
  35.     }  
  36.     KSmartPtr(const KSmartPtr<T>& sp)  
  37.         : m_pData(sp.m_pData)  
  38.         , m_pReference(sp.m_pReference)  
  39.     {  
  40.         m_pReference->AddRef();  
  41.     }  
  42.     ~KSmartPtr(void)  
  43.     {  
  44.         if (m_pReference && m_pReference->Release() == 0)  
  45.         {  
  46.             KSAFE_DELETE(m_pData);  
  47.             KSAFE_DELETE(m_pReference);  
  48.         }  
  49.     }  
  50.     inline T& operator*()  
  51.     {  
  52.         return *m_pData;  
  53.     }  
  54.     inline T* operator->()  
  55.     {  
  56.         return m_pData;  
  57.     }  
  58.     KSmartPtr<T>& operator=(const KSmartPtr<T>& sp)  
  59.     {  
  60.         if (this != &sp)  
  61.         {  
  62.             if (m_pReference && m_pReference->Release() == 0)  
  63.             {  
  64.                 KSAFE_DELETE(m_pData);  
  65.                 KSAFE_DELETE(m_pReference);  
  66.             }  
  67.             m_pData = sp.m_pData;  
  68.             m_pReference = sp.m_pReference;  
  69.             m_pReference->AddRef();  
  70.         }  
  71.         return *this;  
  72.     }  
  73.     KSmartPtr<T>& operator=(T* pValue)  
  74.     {  
  75.         if (m_pReference && m_pReference->Release() == 0)  
  76.         {  
  77.             KSAFE_DELETE(m_pData);  
  78.             KSAFE_DELETE(m_pReference);  
  79.         }  
  80.         m_pData = pValue;  
  81.         m_pReference = new KRefCount;  
  82.         m_pReference->AddRef();  
  83.         return *this;  
  84.     }  
  85.     T* Get()  
  86.     {  
  87.         T* ptr = NULL;          
  88.         ptr = m_pData;  
  89.         return ptr;  
  90.     }  
  91.     void Attach(T* pObject)  
  92.     {  
  93.         if (m_pReference->Release() == 0)  
  94.         {  
  95.             KSAFE_DELETE(m_pData);  
  96.             KSAFE_DELETE(m_pReference);  
  97.         }  
  98.         m_pData = pObject;  
  99.         m_pReference = new KRefCount;  
  100.         m_pReference->AddRef();  
  101.     }  
  102.     T* Detach()  
  103.     {  
  104.         T* ptr = NULL;  
  105.         if (m_pData)  
  106.         {             
  107.             ptr = m_pData;  
  108.             m_pData = NULL;  
  109.             m_pReference->Reset();  
  110.         }  
  111.         return ptr;  
  112.     }  
  113. private:  
  114.     KRefCount* m_pReference;  
  115.     T* m_pData;  
  116. }

重構成本:低。