设计模式:代理模式(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;
}

运行结果:

在这里插入图片描述

在多线程环境下的测试

略。

模式分析

  • 代理模式可以在不改变原始对象接口的情况下,增加额外的逻辑或控制,比如限制某些客户端的访问权限。
  • 代理模式还常⽤在访问真实对象之前或之后执⾏⼀些额外的操作(⽐如记录⽇志),对功能进⾏扩展。

优缺点

优点:

  1. 代理模式可以隐藏真是对象的实现细节,使客户端无需知晓真实对象的工作方式和结构。
  2. 通过代理类来间接访问真实类,可以在不修改真实类的情况下,对其进行扩展、优化或添加安全措施。
  3. 代理模式实现起来简单,易于扩展和维护,符合面向对象设计原则中的开闭原则。

缺点:

  1. 代理模式可能会引入额外的复杂性和间接性,增加程序设计和维护的难度。
  2. 对象代理可能会降低系统性能,特别是在处理大数据量或频繁调用的情况下,因为代理需要额外的计算和网络通信开销。

适用场景

按职责来划分,通常有以下适用场景:

  1. 远程代理: 当一个对象位于不同的地址空间时,代理模式可以提供一个局域代表对象。这在分布式系统中尤其有用,例如,一个客户端可能需要通过代理访问位于远程服务器上的对象。
  2. 虚拟代理: 当创建一个资源消耗很大或者创建过程复杂的对象时,可以使用虚拟代理来延迟对象的创建和初始化,只有在需要时才真正创建并初始化对象。
  3. Copy-on-Write代理:它是虚拟代理的一种,把复制(克隆)操作延迟到只有在客户端真正需要时才执行。一般来说,对象的深克隆是一个开销较大的操作,Copy-on-Write代理可以让这个操作延迟,只有对象被用到的时候才被克隆。
  4. 保护/安全代理:控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限。
  5. 缓冲(Cache)代理:为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。
  6. 防火墙(Firewall)代理:保护目标不让恶意用户接近。
  7. 同步化(Synchronization)代理:使几个用户能够同时使用一个对象而没有冲突。
  8. 智能引用: 智能引用代理可以在调用真实对象的方法时,处理额外的逻辑。例如,代理可以在调用前后添加日志记录、性能监控等。
  9. 外部接口代理: 当使用外部提供的接口(如 STL 或动态库 API 函数)时,由于这些接口可能会发生改变,直接使用可能导致项目与接口函数之间的耦合度很高。通过使用代理模式,可以在项目中用一个类封装这个外部接口,将耦合控制在这个类中,并管理外部接口函数的所有功能。这样,当外部接口函数有变化时,只需要修改代理类中的耦合部分,而无需在整个项目中寻找和修改。

应用场景

  1. AOP:通过定义切面、切入点和通知等,Spring AOP在运行时生成代理对象,将切面逻辑织入到目标对象的方法调用中。代理对象在方法调用前后执行附加操作,如日志记录、性能监控等。
  2. 动态代理(JDK动态代理、CGLIB代理):当Bean类实现了接口时,Spring使用JDK动态代理来为Bean生成代理对象;当Bean类没有实现接口时,Spring使用CGLIB代理来生成代理对象。
  3. Android中的Glide框架使⽤了代理模式来实现图⽚的延迟加载。

代理模式和适配器模式的区别

代理模式的主要⽬的是控制对对象的访问。通常⽤于在访问真实对象时引入⼀些额外的控制逻辑,如权限控制、延迟加载等。

适配器模式的主要目的是使接口不兼容的对象能够协同⼯作。适配器模式允许将⼀个类的接口转换成另⼀个类的接口,使得不同接⼝的类可以协同工作。

代理模式装饰器模式的区别

装饰器模式是为了增强功能,而代理模式是为了加以控制。

模式扩展

在传统的代理模式中,客户端通过Proxy调用RealSubject类的request()方法,同时还在代理类中封装了其他方法(如preRequest()和postRequest()),可以处理一些其他问题。

如果按照这种方法使用代理模式,那么真实主题角色必须是事先已经存在的,并将其作为代理对象的内部成员属性。如果一个真实主题角色必须对应一个代理主题角色,这将导致系统中的类个数急剧增加,因此需要想办法减少系统中类的个数,此外,如何在事先不知道真实主题角色的情况下使用代理主题角色,这都是动态代理需要解决的问题。

动态代理是在程序运行时动态生成代理类,也就是说我们在编写代码时并不知道具体代理的是什么类,而是在程序运行时动态生成。

动态代理的典型应用就是Spring AOP。

参考

  1. https://design-patterns.readthedocs.io/zh-cn/latest/structural_patterns/proxy.html
  2. https://www.runoob.com/design-pattern/proxy-pattern.html
  3. https://blog.csdn.net/weixin_45433817/article/details/131037102
  4. https://www.cnblogs.com/chengmf/p/16035640.html
  5. https://blog.csdn.net/K1_uestc/article/details/135505787
  6. https://blog.csdn.net/weixin_45433817/article/details/130975792
  7. https://blog.csdn.net/h8062651/article/details/136523839
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

UestcXiye

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值