文章目录
一、引言
代理模式是一种结构型设计模式, 让你能够提供对象的替代品或其占位符。 代理控制着对于原对象的访问, 并允许在将请求提交给对象前后进行一些处理。
它的主要思想就是在客户端和真正要访问的对象之间引入一个代理层。于是,以往客户端对真正对象的访问变成了现在通过代理对象进行访问,代理对象在这里起到了一个中介或桥梁的作用。引入代理对象的主要目的是可以为客户端增加额外的功能、约束或针对客户端的调用屏蔽一些复杂的细节问题。
二、代理模式
代理模式的实质是通过引人一个代理类来为原始类(被代理类)增加额外的能力,这些额外的能力可能是指一些新功能、新服务,也可能是一些约束或限制(例如,只有特定用户才能使用原始类)等。
比如我们通过浏览器访问某个网站,最简单的方式就是在浏览器中输入网站的地址来直接访问。首先可以创建一个基类WebAddr
代表要访问的网站,代码如下:
c++
class WebAddr {
public:
virtual void visit() = 0;
virtual ~WebAddr(){}
};
再来两个继承于WebAddr
的子类。
c++
class WebAddr_Shopping:public WebAddr {
public:
virtual void visit() override
{
//访问该购物网站,可能涉及复杂的网络通信
cout << "访问WebAddr_Shopping购物网站!" << endl;
}
};
class WebAddr_Video :public WebAddr {
public:
virtual void visit()override
{
//访问该视频网站,可能涉及复杂的网络通信
cout<< "访问WebAddr_Video视频网站!"<<endl;
}
};
那么我们在访问时,就可以:
c++
unique_ptr<WebAddr> wb1 = make_unique<WebAddr_Shopping>();
wb1->visit();
上面是直接访问某个网站,那么我们引入一个代理类,让代理类帮助我们访问这些网站:
c++
class WebAddrProxy : public WebAddr {
public:
WebAddrProxy(std::shared_ptr<WebAddr> addr) : _addr(addr) {}
void visit() override {
// 可以在此处添加额外的代理逻辑
cout << "代理访问开始..." << endl;
_addr->visit();
cout << "代理访问结束。" << endl;
}
private:
std::shared_ptr<WebAddr> _addr; // 使用智能指针
};
上述代码实现了一个叫作WebAddrProxy
的网站代理类。注意,它仍旧继承自WebAddr
类,该代理类有一个WebAddr
智能指针的成员变量,该成员变量的值是通过WebAddrProxy
类构造函数的初始化列表得到的,这意味着只要所代表的网站不同,调用WebAddrProxy
类的visit
成员函数时就会去访问不同的网站。
c++
shared_ptr<WebAddr> shoppingAddr = make_shared<WebAddr_Shopping>();
shared_ptr<WebAddr> videoAddr = make_shared<WebAddr_Video>();
WebAddrProxy shoppingProxy(shoppingAddr);
WebAddrProxy videoProxy(videoAddr);
shoppingProxy.visit(); // 访问购物网站
videoProxy.visit(); // 访问视频网站
从表面上看,引人代理类WebAddrProxy
似乎让程序代码变得更加复杂,但实际上,引人代理类能够为原始类(WebAddr_Shopping
、WebAddr_Video
)增加额外的能力。就以上述范例来说,可以在WebAddrProxy
类的visit
成员函数中,在代码行 _addr->visit();
的前后增加很多额外的代码来对网站的访问进行额外的限制,例如访问的合法性检查、流量限制、返回数据过滤,等等。WebAddrProxy
类的visit
成员函数可以进行如下功能扩充。
在实际应用中,我们也可以针对某个需要代理的类创建一个专门的代理类:
c++
class WebAddr_Shopping_Proxy :public WebAddr {
public:
virtual void visit() override
{
//在这里进行访问的合法性检查、日志记录或者流量限制...
WebAddr_Shopping* p_webaddr = new WebAddr_Shopping();
p_webaddr->visit();
//在这里可以进行针对返回数据的过滤·
//释放资源
delete p_webaddr;
}
};
代理模式结构
代理模式一般有三种角色:
- 抽象主题 (Subject ):该类定义真实主题与代理主题的共同接口,这样,在使用真实主题的地方都可以使用代理主题,但是后面会讲到,因为代理的种类繁多,目的各异,所以代理主题并不一定必须与真实主题有共同的接口甚至抽象主题也不是必须存在的。这里指
WebAddr
类。 - 代理主题 (Proxy ):该类内部包含了对真实主题的引用,从而可以对真实主题进行访问。代理主题中一般会提供与真实主题相同的接口(这里指
visit
),以达到可以取代真实主题的目的。代理主题可以对真实主题的访问进行约束和限制,也能够控制只在必要的时候才创建/删除真实的主题(一般通过在visit
中增加额外的代码来实现)。这里指WebAddr_Shopping_Proxy
- 真实主题 (RealSubject ):定义代理主题所代表的真实对象,真正的业务是在真实主题中实现的,客户端通过代理主题间接访问真实主题中的接口。这里指
WebAddr_Shopping
类。
引入代理设计模式的定义(实现意图):为其他对象提供一种代理,以控制对这个对象的访问。
三、总结
代理模式的使用方式有很多,延迟初始化 (虚拟代理)。 如果有一个偶尔使用的重量级服务对象, 一直保持该对象运行会消耗系统资源时, 可使用代理模式。 无需在程序启动时就创建该对象, 可将对象的初始化延迟到真正有需要的时候。
访问控制:如果你只希望特定客户端使用服务对象, 这里的对象可以是操作系统中非常重要的部分, 而客户端则是各种已启动的程序 (包括恶意程序), 此时可使用代理模式。 代理可仅在客户端凭据满足要求时将请求传递给服务对象。
代理模式和装饰模式有类似之处,但是这两个模式要解决的问题不同或者说立场不同。代理模式主要是代表真实主题并给真实的主题增加一些新的能力或责任,例如,在这里进行访问的合法性检查、日志记录等,这些功能与真实主题要实现的功能彼此之间可能没有什么相关性,主要是控制对真实主题的访问。而装饰模式是通过引入装饰类来装饰各种构件,为具体构件类增加一些相关的能力,与原有构件能力具有相关性,是对原有构件能力或行为的扩展(增强)。
适配器模式能为被封装对象提供不同的接口;
代理模式能为对象提供相同的接口;
装饰模式则能为对象提供加强的接口。
外观模式与代理的相似之处在于它们都缓存了一个复杂实体并自行对其进行初始化。 代理与其服务对象遵循同一接口, 使得自己和服务对象可以互换, 在这一点上它与外观不同。
代理模式能为对象提供相同的接口;
装饰模式则能为对象提供加强的接口。
外观模式与代理的相似之处在于它们都缓存了一个复杂实体并自行对其进行初始化。 代理与其服务对象遵循同一接口, 使得自己和服务对象可以互换, 在这一点上它与外观不同。
装饰和代理有着相似的结构, 但是其意图却非常不同。 这两个模式的构建都基于组合原则, 也就是说一个对象应该将部分工作委派给另一个对象。 两者之间的不同之处在于代理通常自行管理其服务对象的生命周期, 而装饰的生成则总是由客户端进行控制。