一.意图
代理是一种结构设计模式,允许你为另一个对象提供替代或占位符。代理控制对原始对象的访问,允许你在请求到达原始对象之前或之后执行某些作。
在面向对象系统中,有些对象由于某种原因(比如对象创建的开销很大,或者某些操作需要安全控制,或者需要进程外的访问等),直接访问会给使用者,或者系统结构带来很多麻烦。
对其他对象提供一种代理以控制(隔离,使用接口)对这个对象的访问。------《设计模式》GoF
二.问题
你为什么要控制对某个对象的访问?举个例子:你有一个庞大的物体,消耗了大量系统资源。你偶尔需要它,但并非总是如此。

你可以实现懒惰初始化:只在真正需要时创建这个对象。所有对象客户端都需要执行一些延迟初始化代码。不幸的是,这可能会导致大量代码重复。
理想情况下,我们希望将代码直接放入对象的类中,但这并非总是可行。例如,该类可能是封闭第三方库的一部分。
三.解决方案
代理模式建议你创建一个与原始服务对象接口相同的代理类。然后你更新你的应用,让代理对象传递给原对象的所有客户端。当收到客户端的请求时,代理会创建一个真实的服务对象,并将所有工作委托给它。

但这样做有什么好处呢?如果你需要在类的主逻辑之前或之后执行某件事,代理可以让你做到这一点,而无需更改该类。由于代理实现了与原始类相同的接口,它可以传递给任何期望使用真实服务对象的客户端。
四.现实世界的类比

信用卡是银行账户的代理,银行账户是现金的代理。两者实现了相同的界面:它们可用于支付。消费者会感到安心,因为无需携带大量现金。店主也很满意,因为交易所得的收入会电子化地加入店铺的银行账户,而不会有丢失押金或在去银行途中被抢的风险。
五.结构

六.适合应用场景
有数十种方法可以利用代理模式。让我们来看看最常见的用途。
-
懒惰初始化(虚拟代理)。这指的是你有一个重量级的服务对象,它因为一直开着而浪费系统资源,尽管你只是偶尔需要。
你可以把对象初始化推迟到真正需要的时候,而不是在应用启动时创建对象。
-
访问控制(保护代理)。这时你只希望特定客户端能够使用服务对象;例如,当你的对象是作系统的关键部分,客户端是各种启动的应用程序(包括恶意的)。
代理只有在客户端凭证符合某些条件时,才能将请求转发给服务对象。
-
远程服务(远程代理)的本地执行。这指服务对象位于远程服务器上。
在这种情况下,代理通过网络传递客户端请求,处理与网络合作的所有复杂细节。
-
日志请求(日志代理)。这时你需要保留对服务对象的请求历史。
代理可以在传递给服务之前记录每个请求。
-
缓存请求结果(缓存代理)。这时你需要缓存客户端请求的结果并管理缓存的生命周期,尤其是当结果相当大时。
代理可以为每次请求实现缓存,但这些请求的结果总是相同。代理可以将请求的参数作为缓存键使用。
-
很聪明的推荐。这时你需要在没有客户使用时,能够解雇一个重量级物品。
代理可以跟踪获得服务对象或其结果引用的客户端。代理可能会不时检查客户端,检查它们是否仍然活跃。如果客户端列表为空,代理可能会关闭服务对象并释放底层系统资源。
代理还可以追踪客户端是否修改了服务对象。然后未更改的对象可以被其他客户端重新利用。
六.实现方式
-
如果没有预设的服务接口,就创建一个让代理对象和服务对象可以互换。从服务类中提取接口并不总是可行,因为你需要更改服务的所有客户端才能使用该接口。方案B是让代理成为服务类的子类,这样它就能继承服务的接口。
-
创建代理类。它应该有一个字段来存储对该服务的引用。通常,代理负责创建和管理其服务的整个生命周期。极少数情况下,客户端通过构造函数将服务传递给代理。
-
根据代理方法的目的来实施。在大多数情况下,代理完成一些工作后,应该将工作委托给服务对象。
-
考虑引入一种创建方法,决定客户端是获得代理还是真实服务。这可以是代理类中的简单静态方法,也可以是完整的工厂方法。
-
考虑为服务对象实现懒惰初始化。
七.优缺点
-
优点:
-
你可以在客户端不知情的情况下控制服务对象。
-
当客户端不关心服务对象时,你可以管理服务对象的生命周期。
-
即使服务对象还没准备好或不可用,代理也能正常工作。
-
开闭原则。你可以引入新的代理,而无需更换服务或客户端。
-
-
缺点
-
代码可能会变得更复杂,因为你需要引入很多新类。
-
服务的回复可能会被延迟。
-
八.与其他模式的关系
-
通过适配器,你可以通过不同的接口访问已有的对象。而Proxy的界面保持不变。使用Decorator时,你可以通过增强的界面访问该物品。
-
Facade 与 Proxy 类似,都缓冲复杂实体并独立初始化。与Facade不同,代理与其服务对象具有相同的接口,这使得它们可以互换。
-
装饰者和代理者结构相似,但意图却大相径庭。这两种模式都建立在构图原则之上,即一个对象应将部分工作委托给另一个。区别在于代理通常独立管理其服务对象的生命周期,而装饰器的组成始终由客户端控制。
九.示例代码
#include <iostream>
/**
* The Subject interface declares common operations for both RealSubject and the
* Proxy. As long as the client works with RealSubject using this interface,
* you'll be able to pass it a proxy instead of a real subject.
*/
class Subject {
public:
virtual void Request() const = 0;
};
/**
* The RealSubject contains some core business logic. Usually, RealSubjects are
* capable of doing some useful work which may also be very slow or sensitive -
* e.g. correcting input data. A Proxy can solve these issues without any
* changes to the RealSubject's code.
*/
class RealSubject : public Subject {
public:
void Request() const override {
std::cout << "RealSubject: Handling request.\n";
}
};
/**
* The Proxy has an interface identical to the RealSubject.
*/
class Proxy : public Subject {
/**
* @var RealSubject
*/
private:
RealSubject *real_subject_;
bool CheckAccess() const {
// Some real checks should go here.
std::cout << "Proxy: Checking access prior to firing a real request.\n";
return true;
}
void LogAccess() const {
std::cout << "Proxy: Logging the time of request.\n";
}
/**
* The Proxy maintains a reference to an object of the RealSubject class. It
* can be either lazy-loaded or passed to the Proxy by the client.
*/
public:
Proxy(RealSubject *real_subject) : real_subject_(new RealSubject(*real_subject)) {
}
~Proxy() {
delete real_subject_;
}
/**
* The most common applications of the Proxy pattern are lazy loading,
* caching, controlling the access, logging, etc. A Proxy can perform one of
* these things and then, depending on the result, pass the execution to the
* same method in a linked RealSubject object.
*/
void Request() const override {
if (this->CheckAccess()) {
this->real_subject_->Request();
this->LogAccess();
}
}
};
/**
* The client code is supposed to work with all objects (both subjects and
* proxies) via the Subject interface in order to support both real subjects and
* proxies. In real life, however, clients mostly work with their real subjects
* directly. In this case, to implement the pattern more easily, you can extend
* your proxy from the real subject's class.
*/
void ClientCode(const Subject &subject) {
// ...
subject.Request();
// ...
}
int main() {
std::cout << "Client: Executing the client code with a real subject:\n";
RealSubject *real_subject = new RealSubject;
ClientCode(*real_subject);
std::cout << "\n";
std::cout << "Client: Executing the same client code with a proxy:\n";
Proxy *proxy = new Proxy(real_subject);
ClientCode(*proxy);
delete real_subject;
delete proxy;
return 0;
}
执行结果
Client: Executing the client code with a real subject:
RealSubject: Handling request.
Client: Executing the same client code with a proxy:
Proxy: Checking access prior to firing a real request.
RealSubject: Handling request.
Proxy: Logging the time of request.