设计模式:代理模式(Proxy)
设计模式:代理模式(Proxy)
代理模式(Proxy)属于结构型模式(Structural Pattern)的一种。
结构型模式(Structural Pattern)描述如何将类或者对象结合在一起形成更大的结构,就像搭积木,可以通过简单积木的组合形成复杂的、功能更为强大的结构。
结构型模式可以分为类结构型模式和对象结构型模式:
- 类结构型模式关心类的组合,由多个类可以组合成一个更大的系统,在类结构型模式中一般只存在继承关系和实现关系。
- 对象结构型模式关心类与对象的组合,通过关联关系使得在一个类中定义另一个类的实例对象,然后通过该对象调用其方法。根据“合成复用原则”,在系统中尽量使用关联关系来替代继承关系,因此大部分结构型模式都是对象结构型模式。
模式动机
在某些情况下,一个客户不想或者不能直接引用一个对象,此时可以通过一个称之为“代理”的第三者来实现间接引用。代理对象可以在客户端和目标对象之间起到中介的作用,并且可以通过代理对象去掉客户不能看到 的内容和服务或者添加客户需要的额外服务。
模式定义
代理模式(Proxy)属于对象结构型模式。
代理模式给某一个对象提供一个代理,并由代理对象控制对原对象的引用。
模式结构
代理模式(Proxy)包含如下角色:
- 抽象主题(Subject):定义了真实主题和代理主题的共同接口,这样在任何使用真实主题的地方都可以使用代理主题。
- 真实主题(Real Subject):实现了抽象主题接口,是代理对象所代表的真实对象。客户端直接访问真实主题,但在某些情况下,可以通过代理主题来间接访问。
- 代理(Proxy):实现了抽象主题接口,并持有对真实主题的引用。代理主题通常在真实主题的基础上提供一些额外的功能,例如延迟加载、权限控制、日志记录等。
- 客户端(Client):使用抽象主题接口来操作真实主题或代理主题,不需要知道具体是哪一个实现类。
时序图
模式实现
抽象主题 Subject.h:
#ifndef _SUBJECT_H_
#define _SUBJECT_H_
class Subject
{
public:
virtual void request() = 0;
};
#endif // !_SUBJECT_H_
真实主题 RealSubject.h:
#ifndef _REAL_SUBJECT_H_
#define _REAL_SUBJECT_H_
#include <iostream>
#include "Subject.h"
class RealSubject : public Subject
{
public:
virtual void request()
{
std::cout << "RealSubject::request" << std::endl;
}
};
#endif // !_REAL_SUBJECT_H_
代理 Proxy.h:
#ifndef _PROXY_H_
#define _PROXY_H_
#include <iostream>
#include "Subject.h"
#include "RealSubject.h"
class Proxy : public Subject
{
private:
RealSubject* m_pRealSubject;
void preRequest()
{
std::cout << "Proxy::preRequest" << std::endl;
}
void afterRequest()
{
std::cout << "Proxy::afterRequest" << std::endl;
}
public:
Proxy()
{
m_pRealSubject = new RealSubject();
}
virtual ~Proxy()
{
delete m_pRealSubject;
}
virtual void request()
{
preRequest();
m_pRealSubject->request();
afterRequest();
}
};
#endif // !_PROXY_H_
在单线程环境下的测试
测试代码,也可以说是 client:
#include <stdlib.h>
#include "RealSubject.h"
#include "Proxy.h"
using namespace std;
int main()
{
Proxy* proxy = new Proxy();
RealSubject* realSubject = new RealSubject();
proxy->request();
cout << endl;
realSubject->request();
delete proxy;
delete realSubject;
system("pause");
return 0;
}
运行结果:
在多线程环境下的测试
略。
模式分析
- 代理模式可以在不改变原始对象接口的情况下,增加额外的逻辑或控制,比如限制某些客户端的访问权限。
- 代理模式还常⽤在访问真实对象之前或之后执⾏⼀些额外的操作(⽐如记录⽇志),对功能进⾏扩展。
优缺点
优点:
- 代理模式可以隐藏真是对象的实现细节,使客户端无需知晓真实对象的工作方式和结构。
- 通过代理类来间接访问真实类,可以在不修改真实类的情况下,对其进行扩展、优化或添加安全措施。
- 代理模式实现起来简单,易于扩展和维护,符合面向对象设计原则中的开闭原则。
缺点:
- 代理模式可能会引入额外的复杂性和间接性,增加程序设计和维护的难度。
- 对象代理可能会降低系统性能,特别是在处理大数据量或频繁调用的情况下,因为代理需要额外的计算和网络通信开销。
适用场景
按职责来划分,通常有以下适用场景:
- 远程代理: 当一个对象位于不同的地址空间时,代理模式可以提供一个局域代表对象。这在分布式系统中尤其有用,例如,一个客户端可能需要通过代理访问位于远程服务器上的对象。
- 虚拟代理: 当创建一个资源消耗很大或者创建过程复杂的对象时,可以使用虚拟代理来延迟对象的创建和初始化,只有在需要时才真正创建并初始化对象。
- Copy-on-Write代理:它是虚拟代理的一种,把复制(克隆)操作延迟到只有在客户端真正需要时才执行。一般来说,对象的深克隆是一个开销较大的操作,Copy-on-Write代理可以让这个操作延迟,只有对象被用到的时候才被克隆。
- 保护/安全代理:控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限。
- 缓冲(Cache)代理:为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。
- 防火墙(Firewall)代理:保护目标不让恶意用户接近。
- 同步化(Synchronization)代理:使几个用户能够同时使用一个对象而没有冲突。
- 智能引用: 智能引用代理可以在调用真实对象的方法时,处理额外的逻辑。例如,代理可以在调用前后添加日志记录、性能监控等。
- 外部接口代理: 当使用外部提供的接口(如 STL 或动态库 API 函数)时,由于这些接口可能会发生改变,直接使用可能导致项目与接口函数之间的耦合度很高。通过使用代理模式,可以在项目中用一个类封装这个外部接口,将耦合控制在这个类中,并管理外部接口函数的所有功能。这样,当外部接口函数有变化时,只需要修改代理类中的耦合部分,而无需在整个项目中寻找和修改。
应用场景
- AOP:通过定义切面、切入点和通知等,Spring AOP在运行时生成代理对象,将切面逻辑织入到目标对象的方法调用中。代理对象在方法调用前后执行附加操作,如日志记录、性能监控等。
- 动态代理(JDK动态代理、CGLIB代理):当Bean类实现了接口时,Spring使用JDK动态代理来为Bean生成代理对象;当Bean类没有实现接口时,Spring使用CGLIB代理来生成代理对象。
- Android中的Glide框架使⽤了代理模式来实现图⽚的延迟加载。
代理模式和适配器模式的区别
代理模式的主要⽬的是控制对对象的访问。通常⽤于在访问真实对象时引入⼀些额外的控制逻辑,如权限控制、延迟加载等。
适配器模式的主要目的是使接口不兼容的对象能够协同⼯作。适配器模式允许将⼀个类的接口转换成另⼀个类的接口,使得不同接⼝的类可以协同工作。
代理模式装饰器模式的区别
装饰器模式是为了增强功能,而代理模式是为了加以控制。
模式扩展
在传统的代理模式中,客户端通过Proxy调用RealSubject类的request()方法,同时还在代理类中封装了其他方法(如preRequest()和postRequest()),可以处理一些其他问题。
如果按照这种方法使用代理模式,那么真实主题角色必须是事先已经存在的,并将其作为代理对象的内部成员属性。如果一个真实主题角色必须对应一个代理主题角色,这将导致系统中的类个数急剧增加,因此需要想办法减少系统中类的个数,此外,如何在事先不知道真实主题角色的情况下使用代理主题角色,这都是动态代理需要解决的问题。
动态代理是在程序运行时动态生成代理类,也就是说我们在编写代码时并不知道具体代理的是什么类,而是在程序运行时动态生成。
动态代理的典型应用就是Spring AOP。
参考
- https://design-patterns.readthedocs.io/zh-cn/latest/structural_patterns/proxy.html
- https://www.runoob.com/design-pattern/proxy-pattern.html
- https://blog.csdn.net/weixin_45433817/article/details/131037102
- https://www.cnblogs.com/chengmf/p/16035640.html
- https://blog.csdn.net/K1_uestc/article/details/135505787
- https://blog.csdn.net/weixin_45433817/article/details/130975792
- https://blog.csdn.net/h8062651/article/details/136523839