代理模式简述
定义:是结构型的设计模式之一,它可以为其他对象提供一种代理以控制对这个对象的访问
在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
客户端并不能直接访问真正的主题对象,只能通过代理对象进行间接的访问,这样我们就可通过代理对象来控制对真实主题对象的访问,可以在访问前后做一些动作,比如校验什么之类的动作。
代理模式的实现
代理模式的标准模型图
代理模式的三个角色:
- 抽象角色(Subject):指代理角色和真实角色对外提供的公共方法,一般为一个接口。
- 真实角色(RealSubject):需要实现抽象角色接口,定义了真实角色所要实现的业务逻辑,以便供代理角色调用,也就是真正的业务逻辑。
- 代理角色(Proxy): 需要实现抽象角色接口,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。保存一个引用使得代理可以访问实体。若RealSubject和Subject的接口相同,proxy会引用Subject。提供一个与Subject的接口相同的接口,这样代理就可以用来代替实体。 控制对实体的存取,并可能负责创建和删除它。其它功能依赖于代理的类型。
代码示例:
#include<iostream>
using namespace std;
class Subject// 抽象角色接口
{
public:
virtual void Request() = 0;
};
class RealSubject:virtual public Subject//真实角色
{
public:
void Request()
{
cout << "真实实体请求!" << endl;
}
};
class Proxy:virtual public Subject //代理角色
{
public:
Proxy() :realsubject(NULL) {}
void Request()
{
if (realsubject == NULL)
{
realsubject = new RealSubject();
}
realsubject->Request();//调用真实角色的操作
}
~Proxy()
{
if (realsubject != NULL)
{
delete realsubject;
}
}
private:
RealSubject* realsubject;
};
int main()
{
Proxy* pProxy = new Proxy();
pProxy->Request();
delete pProxy;
return 0;
}
具体的应用示例:
访问真正的服务器,需要通过代理服务器,代理服务器进行用户名密码校验,通过才允许访问真实服务器。
#include <iostream>
using namespace std;
//抽象类,抽象的主题类
class AbstractServer
{
public:
virtual void Request() = 0;
};
//真正主题类,具体提供服务的类
class RealServer :public AbstractServer
{
virtual void Request()
{
cout << "服务器启动..." << endl;
}
};
//代理服务器,非真正的服务器,访问真正服务器必须通过代理服务器
class ProxyServer :public AbstractServer
{
public:
ProxyServer(string name, string pwd)
{
this->name = name;
this->pwd = pwd;
this->server = new RealServer;
}
// 和真正主题类实现共同的接口,对外可以提供一致的接口
virtual void Request()
{
if (!CheckUser())
{
cout << "用户名或者密码错误..." << endl;
return;
}
cout << "请求成功..." << endl;
PreRequest();
this->server->Request();
PostRequest();
}
private:
//访问服务器前进行的动作,可以控制对真实主题类的访问
bool CheckUser()
{
if ("xx" == this->name && "123456" == this->pwd)
{
return true;
}
return false;
}
//真正访问服务器前进行的动作
void PreRequest()
{
cout << "进入代理服务器..." << endl;
}
//访问服务器之后进行的动作
void PostRequest()
{
cout << "服务器访问完毕..." << endl;
}
string name;
string pwd;
private:
AbstractServer* server;
};
//客户端 通过登录代理服务器访问真实服务器
int main()
{
AbstractServer* proxy = new ProxyServer("xx", "123456");//登录代理服务器
proxy->Request();//通过代理服务器 访问真正服务器
return 0;
}
优点:
- 代理模式能将代理对象与真正被调用的对象分离,在一定程度上降低了系统的耦合度。
- 在客户端和目标对象之间,代理起到一个中介作用,这样可以保护目标对象。在对目标对象调用之前,代理对象也可以进行其他操作。
缺点:
- 这种模式引入了另一个抽象层,有时可能是一个问题。如果真实主题被某些客户端直接访问,并且其中一些客户端可能访问代理类,这可能会导致不同的行为。
- 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
- 实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
代理模式的应用场景
根据目的和实现方式的不同,代理模式可分为很多种:
- 虚拟(Virtual)代理:根据需要创建一个资源消耗较大的对象,使得此对象只在需要时才会被真正创建。使用虚拟代理模式的好处就是代理对象可以在必要的时候才将被代理的对象加载;代理可以对加载的过程加以必要的优化。当一个模块的加载十分耗费资源的情况下,虚拟代理的好处就非常明显。
- 远程(Remote)代理:为一个位于不同的地址空间的对象提供一个局域代表对象。这个不同的地址空间可以是在本机器中,也可是在另一台机器中。远程代理又叫做大使(Ambassador)。好处是系统可以将网络的细节隐藏起来,使得客户端不必考虑网络的存在。客户完全可以认为被代理的对象是局域的而不是远程的,而代理对象承担了大部份的网络通讯工作。由于客户可能没有意识到会启动一个耗费时间的远程调用,因此客户没有必要的思想准备。
- 智能引用(SmartReference)代理:当一个对象被引用时,提供一些额外的操作,比如将对此对象调用的次数记录下来等。
- Copy-on-Write代理:虚拟代理的一种。把复制(克隆)拖延到只有在客户端需要时,才真正采取行动。
- 保护(Protector Access)代理:控制对一个对象的访问,如果需要,可以给不同的用户提供不同级别的使用权限。保护代理的好处是它可以在运行时间对用户的有关权限进行检查,然后在核实后决定将调用传递给被代理的对象。
- Cache代理:为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。
- 防火墙(Firewall)代理:保护目标,不让恶意用户接近。
- 同步化(Synchronization)代理:使几个用户能够同时使用一个对象而没有冲突。
在所有种类的代理模式中,虚拟(Virtual)代理、远程(Remote)代理、智能引用代理(SmartReference Proxy)和保护(Protector Access)代理是最为常见的代理模式。