代理 模式

一、什么是代理模式

代理模式指代理控制对其他对象的访问,也就是代理对象控制对原对象的引⽤。在某些情况下,⼀个对象不适合或者不能直接被引⽤访问,⽽代理对象可以在客⼾端和⽬标对象之间起到中介的作⽤。

二、为什么使用代理模式

模式作用:控制和管理对目标对象的访问。

增加额外功能:在不修改真实对象代码的前提下,可以通过代理在访问真实对象时添加额外的功能,如缓存、日志记录、延迟加载、性能监测等。

接口隔离:如果某个对象的接口过于复杂或不希望暴露给客户端,可以使用代理模式提供一个简单的接口进行访问,从而隔离复杂性。

智能引用:代理可以控制对象的生命周期,实现智能指针的功能,比如引用计数,自动释放不再使用的对象等。

代理模式的作用: AOP实现,拦截器,中介,黄牛,解耦,专人做专事。

AOP:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一 种技术。


三、模式的角色

抽象角色:声明真实对象和代理对象的共同接口
代理角色:
1.代理对象角色内部含有对真实对象的引用,从而可以操作真实对象
2.代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象
3.代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装
真实角色:代理角色所代表的真实对象,是我们最终要引用的对象
 

四、代理模式的实现步骤如下:

  • 提供一个抽象主题角色:真实主题与代理主题的共同接口
  • 提供一个真实主题角色:定义了代理角色所代表的真实对象
  • 提供一个代理主题角色:含有对真实主题角色的引用


【解释说明】

代理模式的结构包括⼀个是真正的你要访问的对象(⽬标类)、⼀个是代理对象。⽬标对象与代理对象实现同⼀个接⼝,先访问代理类再通过代理类访问⽬标对象。

五、静态代理和动态代理

静态代理

在编译时就已经确定好了代理类和被代理类的关系。也就是说,在编译时就已经确定了代理类要代理的是哪个被代理类。

真实角色必须是事先已经存在的,并将其作为代理对象的内部属性。但是实际使用时,一个真实角色必须对应一个代理角色,如果大量使用会导致类的急剧膨胀;此外,如果事先并不知道真实角色,该如何使用代理呢?这个问题可以通过Java的动态代理类来解决。

动态代理

在运⾏时才动态⽣成代理类,并将其与被代理类绑定。这意味着,在运⾏时才能确定代理类要代理的是哪个被代理类。

通过这种方式,被代理的对象(RealSubject)可以在运行时动态改变,需要控制的接口(Subject接口)可以在运行时改变,控制的方式(DynamicSubject类)也可以动态改变,从而实现了非常灵活的动态代理关系

代理模式分为静态代理、动态代理:

静态代理指的是,在编译时就已经确定好了代理类和被代理类的关系。也就是说,在编译时就已经确定了代理类要代理的是哪个被代理类。
动态代理指的是,在运⾏时才动态⽣成代理类,并将其与被代理类绑定。这意味着,在运⾏时才能确定代理类要代理的是哪个被代理类。

#include<iostream>
using namespace std;

/* 抽象角色 */
class Subject {
public:
	virtual void request() = 0;
};

/* 真实的角色 */
class RealSubject :public Subject {
public:
	void request() override {
		cout << "RealSubject: request" << endl;
	}
};

/* 静态代理,对具体真实对象直接引用
*  代理角色,代理角色需要有对真实角色的引用,代理做真实角色想做的事情
*/
class ProxySubject :public Subject {
public:
	//除了代理真实角色做该做的事情,代理角色也可以提供附加操作,
	//如:preRequest()和postRequest()
	void request() override {
		preRequest();  
		if (realSubject == NULL) {
			realSubject = new RealSubject(); // 延迟加载:只有在调用方法的时候,才会去new对象
		}
		realSubject->request();
		postRequest(); 
	}
	void preRequest() { }//真实角色操作前的附加操作 
	void postRequest() { }//真实角色操作后的附加操作
private:
	RealSubject *realSubject = NULL;
};

int main() {
	Subject *subject = new ProxySubject();
	subject->request();  //代理者代替真实者做事情
	return 0;
}

六、应用场景(重要!)

1.远程代理(本地执行远程服务):对一个位于不同的地址空间对象提供一个局域代表对象。适用于服务对象位于远程服务器上的情形。
2.虚拟代理(延迟初始化):根据需要将一个资源消耗很大或者比较复杂的对象,延迟加载,在真正 需要的时候才创建;在真实对象创建成功之前虚拟代理 扮演真实对象的替身,而当 真实对象创建之后,虚拟代理将用户的请求转发给真实对象。此时可使用代理模式。

3.保护代理(访问控制):控制对目标对象的访问,给不同的用户提供不同的访问权限即提供不同的操作函数。如果你只希望特定客户端使用服务对象,这里的对象可以是操作系统中非常重要的部分,而客户端则是各种已启动的程序(包括恶意程序),此时可使用代理模式。

4.智能指引(Remote Proxy):取代了简单的指针,它在访问对象时执行一些附加操作。 它的典型用途包括:

  • 对指向实际对象的引用计数,这样当该对象没有引用时,可以自动释放它 (也称为Smart Pointers)。
  • 当第一次引用一个持久对象时,将它装入内存。
  • 在访问一个实际对象前,检查是否已经锁定了它,以确保其他对象不能改变它。

5.Copy-on-Write代理:它是虚拟代理的一种,把复制(克隆)操作延迟到只有在客户端真正需要时才执行。一般来说,对象的深克隆是一个开销较大的操作,Copy-on-Write代理可以让这个操作延迟,只有对象被用到的时候才被克隆。

6.记录日志请求(日志记录代理)。适用于当你需要保存对于服务对象的请求历史记录时。代理可以在向服务传递请求前进行记录。

七、根据应用场景举例(重要!)

7.1 远程代理实例:

我们在国内因为GFW,所以不能访问 facebook,我们可以用翻墙(设置代理)的方法访问。访问过程是:
1.用户把HTTP请求发给代理
2.代理把HTTP请求发给web服务器
3.web服务器把HTTP响应发给代理
4.代理把HTTP响应发回给用户

代理模式在C++中的一个常见应用场景是远程代理,用于在不同的地址空间对象之间进行交互,例如远程方法调用(RMI)。以下是一个简单的远程代理实现的例子:

#include <iostream>
#include <string>
 
// 远程接口
class RemoteService {
public:
    virtual ~RemoteService() {}
    virtual std::string requestData() = 0;
};
 
// 远程服务的具体实现
class ConcreteRemoteService : public RemoteService {
public:
    std::string requestData() override {
        // 实现数据请求的具体逻辑
        return "Data from the remote service";
    }
};
 
// 远程代理类
class RemoteServiceProxy : public RemoteService {
private:
    RemoteService* remoteService;
 
public:
    RemoteServiceProxy() {
        // 真实的远程服务可能需要通过网络或其他方式进行连接
        remoteService = new ConcreteRemoteService();
    }
 
    ~RemoteServiceProxy() {
        delete remoteService;
    }
 
    std::string requestData() override {
        // 在这里可以添加额外的逻辑,例如网络请求的处理
        return remoteService->requestData();
    }
};
 
int main() {
    // 使用代理类进行远程服务调用
    RemoteServiceProxy* proxy = new RemoteServiceProxy();
    std::string data = proxy->requestData();
    std::cout << "Data received: " << data << std::endl;
    delete proxy;
    return 0;
}

在这个例子中,RemoteService定义了远程服务的接口。ConcreteRemoteService是远程服务的具体实现。RemoteServiceProxy是一个代理,它可以连接到远程服务并代为请求数据。在main函数中,我们创建了一个代理实例,并通过它获取数据。这个简单的例子展示了如何使用代理模式来实现远程代理。

7.2 虚拟代理实例:

考虑一个可以在文档中嵌入图形对象的文档编辑器。有些图形对象的创建开销很大。但是打开文档必须很迅速,因此我们在打开文档时应避免一次性创建所有开销很大的对象。这里就可以运用代理模式,在打开文档时,并不打开图形对象,而是打开图形对象的代理以替代真实的图形。待到真正需要打开图形时,仍由代理负责打开。

class Image
{
public:
    Image(string name): m_imageName(name) {}
    virtual ~Image() {}
    virtual void Show() {}
protected:
    string m_imageName;
};
class BigImage: public Image
{
public:
    BigImage(string name):Image(name) {}
    ~BigImage() {}
    void Show() { cout<<"Show big image : "<<m_imageName<<endl; }
};
class BigImageProxy: public Image
{
private:
    BigImage *m_bigImage;
public:
    BigImageProxy(string name):Image(name),m_bigImage(0) {}
    ~BigImageProxy() { delete m_bigImage; }
    void Show() 
    {
        if(m_bigImage == NULL)
            m_bigImage = new BigImage(m_imageName);
        m_bigImage->Show();
    }
};

int main()
{
    Image *image = new BigImageProxy("proxy.jpg"); //代理
    image->Show(); //需要时由代理负责打开
    delete image;
    return 0;
}

7.3 保护代理(访问控制)实例:

使用代理模式控制数据库访问的一个简单C++示例如下:

dbSubject.h抽象主题

#ifndef _DBSUBJECT_H
#define _DBSUBJECT_H

#include <iostream>

// 抽象主题(Subject)
// 数据库接口
class IDatabase {
public:
  virtual void executeQuery(std::string sql) = 0; 
};


#endif

dbRealSubject.h真实主题

#ifndef _DBREALSUBJECT_H
#define _DBREALSUBJECT_H

#include <iostream>
#include "dbSubject.h"

// 真实主题:数据库
class Database : public IDatabase {
public:
  void executeQuery(std::string sql) override {
    // 执行SQL查询
	std::cout << " 执行SQL查询 " <<  sql << std::endl;
  }
};

#endif

dbProxy.h代理

#ifndef _DBPROXY_H
#define _DBPROXY_H

#include <iostream>
#include "dbSubject.h"
#include "dbRealSubject.h"

// 代理 
class DatabaseProxy : public IDatabase {
private:
    Database* db;

public:
    DatabaseProxy() {
        db = new Database();
    }

    void executeQuery(std::string sql) override {
        // 权限校验
        if(checkAccess()) {  
            std::cout << "权限校验: 通过" << std::endl;
            db->executeQuery(sql); 
        } else {
            // 没有访问权限异常 
            std::cout << "权限校验: 未通过" << std::endl;
        }
    }

    // 权限校验
    bool checkAccess() {
        return true;
    }

};

#endif

main.cpp

#include <iostream>
#include <string>

#include "dbSubject.h"
#include "dbProxy.h"

int main() {
    IDatabase* db = new DatabaseProxy();
    db->executeQuery("SELECT * FROM t1");

	return 0;
}

 在这个数据库访问控制的例子中,使用代理模式主要体现了以下几点优势:

  1. 把访问控制逻辑从业务代码中抽象出来,避免了侵入业务逻辑。数据库、业务代码等都不需要变更。
  2. 访问控制逻辑集中到了代理类中,便于维护、扩展和复用。如果要变更控制逻辑,只需要调整代理类的实现。
  3. 使用代理类对数据库访问进行控制,可以进行优化和缓存。比如实现查询数据的缓存,避免每次都直接访问数据库。
  4. 提高了灵活性。代理类可以拦截和控制对实体的访问,而不需要改变实体类的代码。
  5. 可以在不修改原目标类接口的情况下,通过增强代理类扩展目标类的功能。

总结: 代理模式降低了客户端与目标实体之间的耦合,提高了灵活性、扩展性、维护性,使得访问控制逻辑的集中化和重用成为可能。

7.4 智能指针代理

代理模式在C++中的一个常见应用是通过智能指针实现对象的生命周期管理。智能指针可以用作代理来管理资源,比如动态分配的内存,文件句柄等。

以下是一个简单的智能指针代理的例子,它管理着一个动态分配的整数数组的生命周期:

#include <iostream>
#include <memory>
 
class IntArray {
public:
    IntArray(int size) : _size(size) {
        _data = new int[size];
    }
 
    ~IntArray() {
        delete[] _data;
    }
 
    int& operator[](int index)
        return _data[index];
    }
 
    int getSize() const {
        return _size;
    }
 
private:
    int* _data;
    int _size;
};
 
int main() {
    // 使用智能指针来管理动态分配的数组
    std::shared_ptr<IntArray> sp(new IntArray(10));
 
    // 使用数组
    sp->operator[](0) 10;
    std::cout << "First element: " << sp->operator[](0)< std::endl;
 
    return 0;
}

在这个例子中,std::shared_ptr<IntArray> 就是一个智能指针代理,它管理着一个 IntArray 对象的生命周期。当引用计数降为0时,代理会自动删除指向的 IntArray 对象。这样就避免了传统的内存泄漏问题,并且能够自动管理对象的生命周期。

            
参考原文链接:https://blog.csdn.net/m0_56069910/article/details/136674420

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/663745.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MySQL各种锁

目录 1. 从粒度上区分锁 1.1 全局锁&#xff08;第一粒度&#xff09; 1.2 表级锁&#xff08;第二粒度&#xff09; 1.3 行锁&#xff08;第三最小粒度&#xff09; 2 从模式上区分锁 2.1 什么是乐观锁 2.2 什么是悲观锁 2.3 意向共享锁和意向排他锁 2.4 临键锁和记录…

【Python】 深入理解Python中的UnicodeDecodeError及其解决方案

基本原理 在Python编程中&#xff0c;我们经常需要处理各种类型的数据&#xff0c;尤其是文本数据。文本数据在计算机中通常以字节的形式存在&#xff0c;而字节需要被解码成我们能够理解的字符。这个过程涉及到编码和解码的概念。 编码是将字符转换为字节的过程&#xff0c;…

23 vue3面试重难点复习:响应式原理、特点、8大生命钩子、data数据定义、组件、全家桶

vue作为用的最为广泛的当前热门框架&#xff0c;总结如下重难点核心知识&#xff1a; 1.vue特点是什么&#xff1f; 1.1优点 渐进式 vue本身只提供数据响应式&#xff0c;需要全局缓存用 vuex&#xff0c;需要路由用 vue-router 组件化 封装组件&#xff0c;利于复用 响应式数…

k8s——Pod进阶(资源限制和探针)

一、资源限制 1.1 资源限制的定义 当定义Pod时可以选择性地为每个容器设定所需要的资源数量。 最常见的可设定资源是CPU和内存大小&#xff0c;以及其他类型的资源。 当为Pod中的容器指定了request资源时&#xff0c;调度器就使用该信息来决定将Pod调度到哪个节点上。当还为容器…

汇凯金业:量化交易有风险吗

量化交易是一种通过复杂的数学模型和算法在金融市场中进行高频和自动化交易的方式。尽管量化交易在提高市场效率、减少人为错误等方面具有诸多优点&#xff0c;但它也同样存在着不少风险。以下列举了一些主要的风险因素&#xff1a; 1. 模型风险 模型缺陷&#xff1a;量化交易…

网络协议。

一、流程案例 接下来揭秘我要说的大事情&#xff0c;“双十一”。这和我们要讲的网络协议有什么关系呢&#xff1f; 在经济学领域&#xff0c;有个伦纳德里德&#xff08;Leonard E. Read&#xff09;创作的《铅笔的故事》。这个故事通过一个铅笔的诞生过程&#xff0c;来讲述…

数据安全之翼:天空卫士在汽车数据安全领域的卓越领航

近期&#xff0c;中国汽车网络安全与数据安全产业的积极倡导者谈思实验室发布首份《汽车网络与数据安全行业全景图》&#xff0c;天空卫士入选&#xff0c;并且位列榜首。 天空卫士在汽车数据安全领域有丰富的实践经验&#xff0c;曾为多家汽车行业用户提供数据安全产品与服务&…

LeetCode - 贪心(Greedy)算法集合(Python)[分配问题|区间问题]

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/139242199 贪心算法&#xff0c;是在每一步选择中&#xff0c;都采取当前状态下&#xff0c;最好或最优&#xff08;即最有利&#xff09;的选择&…

不同linux账户切换不同的cuda版本

原因 由于服务器中安装了两个版本的cuda&#xff08;cuda10.1和cuda11.1&#xff09;&#xff0c;不同项目可能需要应用不同的cuda版本&#xff0c;但是自己又没有root权限或者只想在使用指定conda环境时改为用指定的cuda版本。总结起来有三种方法&#xff1a; 1、修改软链接指…

正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-24.1,2 SPI驱动实验-SPI协议介绍

前言&#xff1a; 本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM&#xff08;MX6U&#xff09;裸机篇”视频的学习笔记&#xff0c;在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。…

Linux实验六:进程间通信(二)

目录 一、实验目的二、实验内容三、实验环境四、参考代码五、实验步骤步骤1. 编辑源代码test6.c步骤2. 编译源代码test6.c步骤3. 运行可执行程序test6步骤4. 进一步调试源代码test6.c 六、实验结果七、实验总结 一、实验目的 1、理解 POSIX 和 System V 提供的 IPC 相关概念&a…

安防监控视频平台LntonCVS视频监控汇聚平台遏制校园暴力保护校园学生安全应用方案

未成年人被誉为祖国的花朵&#xff0c;是我们国家的未来。然而&#xff0c;最近频繁曝出的未成年霸凌事件却引发了社会的广泛关注。这些事件手段残忍&#xff0c;事态恶劣&#xff0c;引发了全社会对如何保护未成年身心健康、规避霸凌事件发生的深刻思考。 为了更好地保障学生的…

从零开始:如何用Electron将chatgpt-plus.top 打包成EXE文件

文章目录 从零开始&#xff1a;如何用Electron将chatgpt-plus.top 打包成EXE文件准备工作&#xff1a;Node.js和npm国内镜像加速下载初始化你的Electron项目创建你的Electron应用运行你的Electron应用为你的应用设置图标打包成EXE文件结语 从零开始&#xff1a;如何用Electron将…

echarts学习:将echats实例代理为响应式对象可能带来的风险

1.起源 最近我在学习如何封装echarts组件&#xff0c;我所参考的其中一篇博客中提到了一个“图表无法显示的问题”。 根据其中的介绍&#xff0c;造成此种问题的原因是因为&#xff0c;使用ref接受了echarts实例&#xff0c;使得echarts实例被代理为了响应式对象&#xff0c;进…

[C#]使用C#部署yolov8的obb旋转框检测tensorrt模型

【测试通过环境】 win10 x64 vs2019 cuda11.7cudnn8.8.0 TensorRT-8.6.1.6 opencvsharp4.9.0 .NET Framework4.7.2 NVIDIA GeForce RTX 2070 Super 版本和上述环境版本不一样的需要重新编译TensorRtExtern.dll&#xff0c;TensorRtExtern源码地址&#xff1a;TensorRT-CShar…

3D视觉系统实现自动化上下料操作

在竞争激烈的汽车制造行业&#xff0c;提高生产效率、降低成本并保证产品质量是企业持续发展的关键。特别是在汽车制造过程中&#xff0c;各种零部件的上下料操作占据了大量的生产时间&#xff0c;因此如何实现这些操作的自动化、高效化成为了一个亟待解决的问题。 富唯智能3D视…

pom文件中,Maven导入依赖出现 Dependency not found

解决方案&#xff1a; 1、首先看一下自己的Maven是否配置好了 2、再检查一下镜像是否正确 3、如果上面都没有问题&#xff0c;看 dependencyManagement 标签 我这个出错&#xff0c;爆一大片红就是因为 这个标签 dependencyManagement 解决方法&#xff1a;在父工程中进行依…

在 Kubesphere 中开启新一代云原生数仓 Databend

上周六&#xff0c;由 KubeSphere 社区联合 Databend 社区以及纵目科技共同组织的云原生 Meetup 北京站在北京圆满落幕。本次 Meetup 活动邀请到了 SkyWalking PMC 成员、青云科技架构及可观测性团队负责人、江苏纵目科技 APM 研发总监、青云科技容器产品经理、数元灵科技 CTO …

JVM内存划分类加载的过程双亲委派模型的详解

JVM内存划分 JVM也就是java进程&#xff0c;这个进程一旦跑起来就会从操作系统这里申请一大块内存空间&#xff0c;JVM接下来就要进一步的对这个大的空间进行划分&#xff0c;划分成不同区域&#xff0c;从而每个区域都有不同的功能作用&#xff0c;一共分为如下几个区域 1.堆…

【数据结构】二叉树-堆(下)-链式二叉树

个人主页~ 二叉树-堆&#xff08;上&#xff09; 栈和队列 二叉树 四、堆的代码实现Heap.hHeap.ctest.c 五、堆的应用堆排序思想进行排序 六、二叉树链式结构的实现BTree.hBTree.ctest.c 四、堆的代码实现 Heap.h #pragma once#include <stdio.h> #include <stdlib…