代理模式(Proxy)
一、 什么是代理模式
先来看看官方的说法,代理模式就是为其他对象提供一种代理,以控制对这个对象的访问。
看来这个官方的说法的确有点官方,看了还是让人感觉不点不知所措,还是不明白代理模式是什么,究竟是用来做什么的。
其实代理这个名词,对于我们来说其实并不陌生,生活中有很多关于代理的例子。例如校园代理等,就以校园代理来讲,这个校园代理就是为他对应的上司的作代理,而这个校园代理的工作就是访问校园中的学生,例如对学生进行问卷之类的事。在这个例子中,学生就是官方说法中的其他对象,校园代理的上司就通过控制这个校园代理来控制对学生的访问。这下应该明白一点了吧。
二、 代理模式的类图
三、 代理模式的实现代码(C++实现)
以上面说的校园代理为例子,我们来一睹代理模式的风采,为了更好地理解这个模式,还是要作一点的解释的。在下面的代码中,将出现三个类,Worker、Boss和SchoolProxy,他们分别对应上图的Subject、RealSubject和Proxy这三个类,而这些类中的doSurvey方法,就是相当于上图中的Request方法。
注:其实上图只是一个代理模式的UML模型图,Request其实是代表着所有Proxy和RealSubject的共用接口,而不仅仅是这里所写的一个。
其实现代码如下(proxy.cpp):
1、Worker类的定义如下,它有一个doSurvey的接口
class Worker { public: virtual void doSurvey() = 0; virtual ~Worker(){} };
2、Boss类定义如下,它继承Worker类,并实现doSurvey接口
class Boss : public Worker { public: virtual void doSurvey() { cout << "The ABCDE company do sruvey!" << endl; } };
3、SchoolProxy类定义如下,它同样继承Worker类,并实现doSurvey接口,该类维护一个Boss类的对象的引用,并在它的中doSurvey方法中调用Boss的doSurvey方法。
class SchoolProxy : public Worker { public: SchoolProxy(): _boss(NULL) { } virtual ~SchoolProxy() { if(_boss != NULL) { delete _boss; } } SchoolProxy(const SchoolProxy&proxy) { _boss = newBoss(*proxy._boss); } SchoolProxy& operator=(const SchoolProxy &rhs) { if(this != &rhs) { SchoolProxytmp_proxy(rhs); Boss *tmp_boss =tmp_proxy._boss; tmp_proxy._boss = _boss; _boss = tmp_boss; } return *this; } virtual void doSurvey() { if(_boss == NULL) { _boss = new Boss(); } _boss->doSurvey(); } private: Boss *_boss; };
注:此类中的Boss也可不使用指针,而直接使用对象。但是因为java或C#这类的语言并不支持栈上对象,所有的对象都是new出来的,所以这样的写法与java和C#更相似。
4、调用方法如下:
int main() { SchoolProxy *proxy = new SchoolProxy(); proxy->doSurvey(); delete proxy; return 0; }从上面的代码,我们可以清晰地看到,校园代理SchoolProxy是如何帮助它的Boss来完成做调查的工作的。
四、 代理模式的应用
看了上面的代码,我想代理模式的操作和原理,大家都差不多可以理解了,但是其实代理还是有不只一种的,根据通常的使用,可以分为四类。
1) 远程代理
它为一个对象在不同的地址空间提供局部代表。这样就可以隐藏一个对象存在于不同地址空间的事实,它的例子就是WebServer。
2) 虚拟代理
它根据需求创建开销比较大的对象,通过它来存放实例化需要很长时间的真实对象。例如,当我们浏览网页时,网页中可能有一些比较大的图片,虽然图片比较大,但是你还是可以很快地打开网页,然而图片却不能在第一时间显示,可能要在几秒后才能正常显示,在这里就是通过虚拟代理来替换真实的图片。
3) 保护代理
它用于控制对原对象的访问,保护代理用于对象应该有不同的访问权限的时候。
4) 智能引用
它用于指当调用真实对象时,代理执行额外的一些操作,处理另外一些事情。例如C++中的智能指针,它取代了简单的指针,它会对它所指向的对象执行一些额外的操作。
五、 代理模式的真实应用之share_ptr
看了上面的代码,看了上面的解说,你可能觉得代理模式没什么作用,那么你就错了,下面来看看,上面四种代理模式其中一种智能引用的强大用处。
有使用过C++的程序员,肯定知道智能指针这个好东西,它可以让大大减少我们对内存的管理难度,因为它通过以对象管理资源的方法,使指针指向向的堆内存像栈内存一样,可以自动释放。
下面就以本人实现的share_ptr来说明智能引用的代理模式的应用,首先来看看类的定义:
template <typename T> class SharePtr { public: SharePtr(T *tptr = NULL); SharePtr(const SharePtr &sptr); SharePtr& operator=(const SharePtr &sptr); SharePtr& operator=(T *tptr); ~SharePtr(); T&operator*()const; T*operator->()const; bool operator==(const SharePtr &sptr)const; bool operator!=(const SharePtr &sptr)const; bool operator==(const T *tptr)const; bool operator!=(const T *tptr)const; const T* getPtr()const; private://function void _decUsed(); inline void _nullTest()const; inline bool _isSame(const SharePtr &sptr)const; inline bool _isSame(const T *tptr)const; private://data T *_ptr; size_t *_used;//引用计数,为0时释放ptr指向的对象 };
它的定义就只有这么多了,看了上面的定义,你可以会产生疑问,这个真的应用了上面所说的代理模式吗?上面的代理模式不是要有一个Subject类、一个RealSubject类和一个Proxy类的吗?而RealSubject类和Proxy是Subject的子类,需要定义Subject的接口的吗?为什么这里只有一个类呢?
现在你看不出来,我并不怪你。看下去自然会明白。
智能指针,这里以share_ptr为例,它的目的就是通过一个类来模仿一个指针的行为,并提供指针没有的功能,就是当指针变量出了作用域后,自动处理指针指向的内存的功能。所以上面你看到的SharePtr模板类,就相当于Proxy类。
那RealSubject类呢?因为智能指针是指针的代理,那么RealSubject类当然就是SharePtr<T>中的成员变量T,我觉得说是T类型的指针T*更加恰当。哈哈,奇妙吧!
那么Subject类呢?这里是什么充当这个类呢?从代理模式的类图可以看到Subject类定义的是RealSubject和Proxy的共有接口,你想想看,既然RealSubject是一个指针,那么它的操作就是*、->、==、!=和 = 这五种了,由于这些操作本来就是原生的操作,并不是什么特别的规定,所以在这里并没有这个Subject,但是可以看到,这些共有的接口或者说是操作,上面的SharePtr类中,还是有实现的。
换一个角度来说,*、->、==、!=和 =这五类操作就是Subject类中的接口,但是在这里并没有必要实现一个Subject的基类,然后让SharePtr来继承它。因为指针并不需要继承Subject就已经具有这五类操作了。所以这里把这个类省去了。
这下你应该明白,为什么这里的一个类就是一个代理模式了吧。其实我觉得学习设计模式最重要的还是学习它的思想,学习的解决问题的方法以及如何用它架构我们的程序,而不是对照着类图或者它的定义来生搬硬套。上面的智能指针就是一个例子。
其实智能指针并不是一个指针变量,它只是一个定义在栈上的对象,通过运算符重载使其行为像一个指针变量。当程序运行出了其作用域后,就会析构销毁,执行相应的操作。下面再来简单看看,这个代理为我们做了一些什么额外的事情。
例如,当对象释放时,会进行如下操作:
template <typename T> SharePtr<T>::~SharePtr() { _decUsed(); } template <typename T> void SharePtr<T>::_decUsed() { --*_used; if(*_used ==0) { if(_ptr!= NULL) { delete _ptr; _ptr= NULL; } delete _used; _used = NULL; } }
更多的实现和额外操作,可查看源代码。
六、 Android中的代理模式
在Android中代理模式也是使用广泛的,例如ActivityManagerProxy类就是一个代理,它是ActivityManagerNative的代理,也就是说ActivityManagerProxy是上面所说的Proxy类,而ActivityManagerNative就相当于RealSubject类,它们都有一个共有的接口IActivityManager。
在这里还有一个重要的类:ActivityManager,它相当于代理模式的类图中的client。在这个类中,可以看到大量的getxxx函数,这些函数,都会调用到ActivityManagerNative类的getDefault()方法,而该方法会获得一个共用的单例的IActivityManager引用,然后通过多态来调用代理中的实现。
注:由于时间的关系,没有深入去研究。
七、 代码地址
http://download.csdn.net/detail/ljianhui/7468509