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

重构成本:低。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值