九
17
定义
代理(Proxy)模式,为其他对象提供一种代理以控制对这个对象的访问。
结构

理解
1. Subject定义了RealSubject和Proxy的公用接口,使得任何调用RealSubject的地方都可以调用Proxy。
2. RealSubject是代理的实体类。
3. Proxy保存一直指向代理实体(RealSubject)的指针。Proxy与Subject的接口相同,代理就可以替代实体;Proxy负责RealSubject的创建和管理。
要点
1. 根据代理模式效果的不同,有如下几种代理应用:
远程代理(Remote Proxy)
为一个位于不同的地址空间的对象提供隐藏实现。这个不同的地址空间可以是在本机器中,也可是在另一台机器中。远程代理又叫做大使(Ambassador)。
远程调用和webservice服务就是典型的远程代理模式,客户并不知道网络访问细节(访问本地还是远程网络),代理对象对外屏蔽了网络通讯细则。
虚拟代理(Virtual Proxy)
根据需要创建一个资源消耗较大的对象,使得此对象只在需要时才会被真正创建。使用虚拟代理模式的好处就是代理对象可以在必要的时候才将被代理的对象加载;代理可以对加载的过程加以必要的优化。
文档中有非常大的图片,为了保证文档打开速度,在打开文档的时候并不真正创建图片对象,只有当用到图片的时候,才会去创建图片对象。
写时拷贝代理(Copy-on-write Proxy)
写时拷贝是虚拟代理的一种特殊应用,可以大幅降低拷贝大对象的开销。写时拷贝可以延迟一个对象的拷贝操作到客户调用里。一般来说,对象的深度拷贝是一个高开销的动作,该代理可以让这个动作延迟,只有对象被修改的时候才被拷贝一份。
Linux创建进程就用到了Copy-on-write。fork派生子进程的时候,若子进程拷贝父进程的整个地址空间,会使得创建进程的速度非常慢而且效率低下。而实事上,子进程几乎不必读和修改父进程所拥有的资源, Copy-on-write允许父子进程拥有相同的物理页,只有两者中有一个试图修改一个物理页时,内核就把要修改的物理页内容拷贝到一个新的物理页,并把这个新的物理页地址分配给正在写的进程。
保护代理(Protection Proxy)
控制对RealSubject对象的访问。可以实现给不同的用户提供不同级别的使用权限。保护代理可以在运行时间对用户的有关权限进行检查,然后在核实后决定将调用传递给被代理的对象。
智能引用代理(Smart Reference Proxy)
取代了简单的指针(->, *),在对象访问时提供一些附加操作:对指向实际对象的引用计数,引用计数为0时,可以自动释放资源(智能指针);当第一次引用一个持久的对象,将它装入内存;在访问一个实际对象前,检查是否已经锁定了它,以确保其他对象不能改变它。
缓存代理(Cache Proxy)
为创建时高开销的对象提供缓存,以供多客户端共享。如数据库代理,客户端进行Sql查询时,并不访问数据库,而是通过与数据库代理之间进行交互,如果数据库代理中没有缓存数据,才真正去数据库查询。
防火墙代理(Firewall Proxy)
保护目标对象不受某些不良客户端的侵害。
同步代理(Synchronization Proxy)
使几个用户能够同时使用一个对象而没有冲突,提供并发控制。
2. C++语言可通过重载->和*运算符实现类的代理。
Image* ImagePtr::operator-> () {
return LoadImage(); //装载对象并返回对象指针
}
这样对象被引用时,就装载对象,起到了代理的作用。但这种方式的问题是,只要引用就加载,而不能指定在调用某个方法(如Draw)的时候才加载。
应用
每种代理模式的应用场景都非常之多。共同点是代理起到了客户端与真实对象之间中间层的作用。通过对中间层进行改造,到达各种指责目的。
源码通过缓存代理应用来举例说明。
源码
#include <iostream>
#include <string>
#include <map>
using namespace std;
//代理抽象基类
class CProxy
{
public:
CProxy() {};
virtual ~CProxy() {};
virtual string Get(const string &strKey) = 0;
};
//代理的实际目标
class CDataServer : public CProxy
{
public:
CDataServer() {};
virtual ~CDataServer() {};
virtual string Get(const string &strKey)
{
//TODO: query db here…
cout << "real server query db…" << endl;
return strKey + strKey;
}
};
//代理类
class CCacheProxy : public CProxy
{
public:
CCacheProxy() : m_dataServer(new CDataServer)
{};
virtual ~CCacheProxy()
{
delete m_dataServer;
};
virtual string Get(const string &strKey)
{
//target cache
map<string, string>::iterator itr = m_mapValue.find(strKey);
if (itr != m_mapValue.end())
{
cout << "target cache…" << endl;
return itr->second;
}
//request from real server
string strVal = m_dataServer->Get(strKey);
m_mapValue[strKey] = strVal;
return strVal;
}
protected:
CDataServer* m_dataServer;
map<string, string> m_mapValue; //cache
};
int main()
{
//client并不知道CDataServer
CProxy* p = new CCacheProxy;
string strKey = "ab";
string strVal = p->Get(strKey);
cout << "key: " << strKey << ", value: " << strVal << endl;
strKey = "abcd";
strVal = p->Get(strKey);
cout << "key: " << strKey << ", value: " << strVal << endl;
strKey = "ab";
strVal = p->Get(strKey);
cout << "key: " << strKey << ", value: " << strVal << endl;
return 0;
}
转载本站文章请注明,转载自:神秘果
本文链接: http://www.shenmiguo.com/archives/2009/314_design-patterns-proxy.html

Leave a Reply