一、概述
1、代理模式:给某一个对象提供一个代理或占位符,并由代理对象来控制对原对象的访问。
2、代理对象在客户端和目标对象之间起到中介作用
3、引入一个新的代理对象,代理模式的主要目的是在不改变原始对象接口的前提下,增加额外的逻辑或控制
4、去掉客户不能看到的内容和服务或者增添客户需要的额外的新服务
二、代理模式的结构
代理模式包含以下三个角色:
1、Subject(抽象主题角色):定义了实际对象(RealSubject)和代理对象(Proxy)共同实现的接口。这个接口定义了实际对象的行为
2、Proxy(代理主题角色):同样实现了Subject接口,但它并不直接实现功能。相反,它持有(RealSubject)的引用,并在需要时代表(RealSubject)执行操作。代理对象可以在调用(RealSubject)的方法之前或之后,添加额外的逻辑,例如权限检查等。
3、RealSubject(真实主题角色):实现了Subject接口,定义了实际的功能
三、UML图
四、代理模式实现的步骤
1、定义接口(Subject)
首先,定义一个接口类,该接口声明了代理类和目标类需要实现的公共方法。这个接口定义了代理模式的通用约定,确保客户端代码可以一致地与代理类和目标类进行交互。
class Subject
{
public:virtual ~Subject() = default;virtual void request() const = 0;
};
2、创建被代理类(RealSubject)
其次,创建被代理类(RealSubject),并实现接口中声明的方法。被代理类负责实际的功能实现,并包含客户端所需的具体业务逻辑
class RealSubject : public Subject
{
public:void request() const override{//实现具体的业务逻辑cout << "RealSubject: Handling request" << endl;}
};
3、创建代理类(ProxySubject)
然后,创建需要的代理类,代理类同样实现定义的接口。在代理类的方法中,可以添加额外的逻辑或控制。这些额外的逻辑可以在调用被代理类方法之前或之后。
class Proxy : public Subject
{
public:Proxy() : m_realSubject(std::make_shared<RealSubject>()){}void request() const override{cout << "Proxy : Logging request" << endl;m_realSubject->request();cout << "record : Logging request" << endl;}private:std::shared_ptr<RealSubject> m_realSubject;
};
4、使用代理
最后,客户端代码通过代理类与被代理类交互。客户端不需要知道代理类的存在,只需要关心接口定义的方法。代理类负责在客户端与被代理类之间传递请求,并在必要时添加额外的逻辑或控制
int main()
{std::shared_ptr<Subject> proxy = std::make_shared<Proxy>();proxy->request();return 0;
}
四、代理模式的应用场景
1、远程代理:在分布式系统中,远程代理可以代表远程对象的本地代理,从而隐藏远程访问的复杂性。它使得客户端可以像使用本地对象一样使用远程对象
2、延迟加载(懒加载):当一个对象的创建成本很高,或者它的初始化过程很复杂时,可以使用代理模式来延迟对象的创建。代理对象在真正需要时才会创建实际对象,从而提高系统的性能和效率。
3、访问控制:代理模式可以控制对真实对象的访问权限。例如,可以在代理中添加权限检查,以确保只有授权用户才能访问真实对象。这在系统安全性要求较高的场景中尤为重要。
4、缓存:代理可以在内存中缓存真实对象的结果,以提高性能。例如,网络请求的代理可以缓存之前的请求结果,以避免重复的网络访问。
5、日志记录:在代理对象中添加日志记录功能,记录对真实对象的访问情况。这对于调试和监控系统行为非常有用。
6、智能引用:代理模式可以提供对真实对象的智能引用,例如引用计数。当多个代理对象共享一个真实对象时,代理可以负责管理真实对象的生命周期,确保在不再需要时正确释放资源。
7、虚拟代理:当真实对象的创建开销很大或很复杂时,虚拟代理可以提供一个虚拟的对象占位符,直到真正需要对象时才会进行创建。
五、代理模式应用于远程代理
远程代理的实现中,代理模式通常会创建一个本地代理对象,该对象充当与远程对象进行交互的中介。这个本地代理对象负责处理与远程对象的通信,包括数据的序列化和反序列化、通过网络发送请求以及接收响应等。下面是一个简单的 C++ 示例,演示了如何使用代理模式来实现远程代理:
1、定义接口
首先,定义一个接口,声明远程对象和本地代理都需要实现的方法
class RemoteSubject
{
public:virtual ~RemoteSubject() = default;virtual void request() const = 0;
};
2、实现目标对象
通常,目标对象是远程服务器上实际存在的类。由于这个目标对象在本地不可直接访问,我们通常会将其实现放在服务器端。
class RealRemoteSubject : public RemoteSubject
{
public:void request() const override{cout << "RealRemoteSubject: Handling request on server" << endl;}//服务器端代码通常会包括网络通信的设备和管理,但这里为了简化示例,省略了这些部分
};
3、实现代理类
代理类在客户端与远程对象之间进行中介,处理与远程对象的通信细节
//假设我们有一个简单的网络通信库来发送请求到服务器
class NetWorkClient
{
public:void sendRequest() const{cout << "NetWorkClient: sending request to the server..." << endl;}void receiveResponse() const{cout << "NetWorkClient: Reveiving response from the server..." << endl;}
};class ProxyRemoteSubject : public RemoteSubject
{
public:ProxyRemoteSubject() : m_netWorkClient(std::make_shared<NetWorkClient>()) {}void request() const override{//代理类负责将请求转发到远程对象m_netWorkClient->sendRequest();//在实际应用中,可能需要序列化请求数据,并通过网络发送//网络操作可能会包括使用 socket、HTTP请求等cout << "ProxyRemoteSubject: Request forwarded to the server..." << endl;//接受远程服务器的响应m_netWorkClient->receiveResponse();//在实际应用中,可能需要处理响应数据//比如解析服务器返回的数据,或将响应转换为结果}
private:std::shared_ptr<NetWorkClient> m_netWorkClient;
};
4、客户端代码
客户端通过代理类与远程对象交互,代理类处理所有的网络通信细节
int main()
{std::shared_ptr< RemoteSubject> proxy = std::make_shared<ProxyRemoteSubject>();proxy->request();
}
5、总结
在远程代理模式中,ProxyRemoteSubject
类的作用是充当客户端和远程象 RealRemoteSubject
之间的中介。它不直接处理 RealRemoteSubject
的方法调用,而是通过网络通信将请求转发给远程服务器上的 RealRemoteSubject
。
六、代理模式应用于访问控制
#include <iostream>
#include <memory>
#include <string>// 定义接口
class Subject {
public:virtual ~Subject() = default;virtual void request() const = 0;
};// 目标对象
class RealSubject : public Subject {
public:void request() const override {std::cout << "RealSubject: Handling request." << std::endl;}
};// 保护代理
class ProtectionProxy : public Subject {
private:std::shared_ptr<RealSubject> realSubject;std::string userRole;public:ProtectionProxy(const std::string& role) : realSubject(std::make_shared<RealSubject>()), userRole(role) {}void request() const override {if (userRole == "admin") {realSubject->request();} else {std::cout << "ProtectionProxy: Access denied." << std::endl;}}
};int main() {std::shared_ptr<Subject> proxy1 = std::make_shared<ProtectionProxy>("admin");proxy1->request();std::shared_ptr<Subject> proxy2 = std::make_shared<ProtectionProxy>("user");proxy2->request();return 0;
}
七、模式优点
1、能够协调调用者和被调用者,在一定程度上降低了系统的耦合度
2、客户端可以针对抽象主题角色进行编程,增加和更好代理类无须修改源代码,符合开闭原则,系统具有更好的灵活性和可扩展性
八、模式缺点
1、由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢(例如保护代理)
2、实现代理模式需要额外的工作,而且有些代理模式的实现过程较为复杂(列如远程代理)