设计模式之代理模式(Proxy),以C++为例,实现远程代理、虚拟代理、保护代理等。

2023-05-16

        兄弟姐妹们好,又是好久没有更新了,今天给大家简单介绍代理模式,一个很简单的设计模式,旨在不改变原对象的情况下通过代理对象来控制对原对象的访问。代理模式根据具体情况还可以分为远程代理、虚拟代理、保护代理等,下面来介绍一下。

目录

 一、代理模式基础介绍

1.1 基础

1.2 代码示例

二、远程代理(Remote proxy)

三、虚拟代理(Virtual Proxy)

四、保护代理(Protective Proxy)

五、缓存代理(Cache Proxy)

六、总结


 一、代理模式基础介绍

1.1 基础

        C++代理模式是一种结构型设计模式,其主要目的是为其他对象提供一种代理,以控制对这些对象的访问。代理对象可以充当原始对象的包装器,将请求转发到原始对象,并且可以在转发请求之前或之后执行一些额外的操作。

        代理模式通常用于以下几种情况:

  1. 远程代理:用于在不同地址空间中的两个对象之间通信,将请求发送到远程对象。
  2. 虚拟代理:用于延迟加载,即在需要时加载资源。

  3. 保护代理:用于控制对对象的访问权限,例如,只有特定用户才能访问某个对象。

  4. 缓存代理:用于缓存对象的访问结果,以避免重复执行计算密集型操作。

        在C++中实现代理模式可以使用抽象类和接口来定义代理和原始对象之间的通信协议,并使用具体类来实现它们。代理对象将请求转发给原始对象,并可以在转发请求之前或之后执行一些额外的操作。

1.2 代码示例

        以下是一个简单的C++代理模式的示例代码:

#include <iostream>
using namespace std;

// 定义抽象类 Subject,代表原始对象和代理对象共同的接口
class Subject {
public:
    virtual void request() = 0;
};

// 定义具体类 RealSubject,实现原始对象的功能
class RealSubject : public Subject {
public:
    void request() override {
        cout << "RealSubject: Handling request." << endl;
    }
};

// 定义代理类 Proxy,实现代理对象的功能
class Proxy : public Subject {
private:
    RealSubject* real_subject_;

    void check_access() const {
        cout << "Proxy: Checking access prior to handling request." << endl;
    }

public:
    Proxy(RealSubject* real_subject) : real_subject_(real_subject) {}

    void request() override {
        check_access();
        real_subject_->request();
    }
};

int main() {
    RealSubject* real_subject = new RealSubject;
    Proxy* proxy = new Proxy(real_subject);
    proxy->request();

    delete proxy;
    delete real_subject;
    return 0;
}

        在这个示例中,抽象类 Subject 定义了原始对象和代理对象共同的接口 request(),具体类 RealSubject 实现了原始对象的功能,代理类 Proxy 实现了代理对象的功能,并在转发请求之前执行了一个额外的操作 check_access()。在 main() 函数中,创建了一个 RealSubject 对象和一个 Proxy 对象,并通过 Proxy 对象调用了 request() 方法。

        类图如下:

 二、远程代理(Remote proxy)

        C++远程代理是一种设计模式,它允许客户端通过代理对象间接访问远程服务或对象。这个模式通常被用于网络编程中,比如RPC(远程过程调用)分布式系统中

// 假设这是远程服务端对象的头文件
class RemoteService {
public:
    virtual void foo() = 0;
};

// 代理类,用于访问远程服务端对象
class RemoteServiceProxy : public RemoteService {
public:
    RemoteServiceProxy(const std::string& host, int port) : m_host(host), m_port(port) {}

    void foo() override {
        // 连接远程服务端
        connect();

        // 向远程服务端发送请求
        sendRequest("foo");

        // 等待远程服务端响应
        std::string response = receiveResponse();

        // 关闭连接
        disconnect();

        // 处理响应
        processResponse(response);
    }

private:
    std::string m_host;
    int m_port;
    int m_socketFd;  // 保存套接字描述符,用于连接远程服务端

    void connect() {
        // 连接远程服务端代码
    }

    void sendRequest(const std::string& request) {
        // 向远程服务端发送请求代码
    }

    std::string receiveResponse() {
        // 从远程服务端接收响应代码
    }

    void disconnect() {
        // 关闭连接代码
    }

    void processResponse(const std::string& response) {
        // 处理响应代码
    }
};

// 客户端代码
int main() {
    RemoteServiceProxy proxy("127.0.0.1", 8080);
    proxy.foo();  // 通过代理对象间接访问远程服务端对象的foo()方法
    return 0;
}

        上述代码中,RemoteService是一个抽象基类,它定义了远程服务端对象的接口。RemoteServiceProxy是代理类,它通过套接字描述符连接远程服务端,并将客户端的请求转发给远程服务端。客户端只需要通过代理对象访问远程服务端的方法即可,而无需知道远程服务端的具体实现细节。

        类图如下:

三、虚拟代理(Virtual Proxy)

        C++虚拟代理(Virtual Proxy)模式是一种结构型设计模式,它允许你创建一个代理对象来代替一个真实对象。该代理对象可以控制对真实对象的访问,并在需要时才创建或加载真实对象。

        代码示例:

#include <iostream>
using namespace std;

// 定义一个抽象类Subject
class Subject {
public:
    virtual void request() = 0;
};

// 定义一个真实的Subject类RealSubject
class RealSubject : public Subject {
public:
    void request() {
        cout << "真实的请求" << endl;
    }
};

// 定义一个代理类Proxy
class Proxy : public Subject {
private:
    RealSubject *realSubject;

public:
    void request() {
        if (realSubject == nullptr) {
            realSubject = new RealSubject();
        }
        cout << "代理请求" << endl;
        realSubject->request();
    }
};

// 客户端代码
int main() {
    Proxy proxy;
    proxy.request();
    return 0;
}

        在上面的示例中,我们定义了一个抽象类Subject和一个具体的类RealSubject,它们都实现了request()方法。然后我们定义了一个代理类Proxy,它也实现了request()方法,但是它首先检查真实的主题是否已经创建,如果没有创建,则创建一个。然后代理类打印一条消息,表示代理请求。最后,它调用真实主题的request()方法。

        在客户端代码中,我们实例化了一个代理对象,并调用了request()方法。由于我们使用了代理模式,所以当我们调用代理对象的request()方法时,它将首先创建真实对象(如果没有创建),然后打印代理请求,最后调用真实对象的request()方法。

        这种模式的一个好处是,它可以延迟创建真实对象的时间,直到它真正需要使用。这可以节省资源,特别是当创建真实对象的代价很大时。

        类图:

四、保护代理(Protective Proxy)

        C++中的保护代理(Protective Proxy)是一种结构型设计模式,其目的是控制对对象的访问。它使用一个代理对象来控制原始对象的访问,代理对象通过限制或控制原始对象的访问来提供额外的安全性和保护。

#include <iostream>
#include <string>
#include <memory>

class Image {
public:
    Image(std::string name) : name_(name) {}
    virtual void Display() = 0;
    virtual ~Image() {}

protected:
    std::string name_;
};

class RealImage : public Image {
public:
    RealImage(std::string name) : Image(name) {
        LoadFromDisk();
    }

    void Display() override {
        std::cout << "Displaying " << name_ << std::endl;
    }

private:
    void LoadFromDisk() {
        std::cout << "Loading " << name_ << " from disk" << std::endl;
    }
};

class ImageProxy : public Image {
public:
    ImageProxy(std::string name) : Image(name) {}

    void Display() override {
        if (real_image_ == nullptr) {
            real_image_ = std::make_unique<RealImage>(name_);
        }
        real_image_->Display();
    }

private:
    std::unique_ptr<RealImage> real_image_;
};

int main() {
    // Create a real image object
    auto real_image = std::make_unique<RealImage>("image1.jpg");
    
    // Display the real image
    real_image->Display();

    // Create an image proxy object
    auto image_proxy = std::make_unique<ImageProxy>("image2.jpg");

    // Display the image proxy
    image_proxy->Display();

    // The real image is only loaded once, even though it is displayed twice
    return 0;
}

        在上面的示例代码中,Image 是一个抽象基类,RealImage 和 ImageProxy 是具体的类。RealImage 是一个真实的图像对象,它可以从磁盘中加载并显示图像。ImageProxy 是一个代理对象,它可以访问真实图像对象,并负责加载和显示真实图像对象。当我们调用 ImageProxy 对象的 Display() 方法时,它会首先检查是否已经加载了真实图像对象。如果没有加载,它将使用 RealImage 对象加载图像。这种方式可以减少对真实图像对象的频繁访问,从而提高程序的效率。

        类图:

五、缓存代理(Cache Proxy)

        缓存代理模式的基本思路是:为了避免每次调用一个函数或方法时都要进行重复的计算,我们可以将其结果缓存起来,下次需要时就可以直接返回缓存的结果,而不用再进行计算。

#include <iostream>
#include <unordered_map>

using namespace std;

// 定义一个全局的缓存代理类
class FactorialCacheProxy {
public:
    int getFactorial(int n) {
        if (cache_.find(n) != cache_.end()) {
            // 如果结果已经被缓存,直接返回缓存的结果
            cout << "Get result from cache: " << n << endl;
            return cache_[n];
        } else {
            // 如果结果没有被缓存,进行计算并缓存结果
            cout << "Calculate result: " << n << endl;
            int result = calculateFactorial(n);
            cache_[n] = result;
            return result;
        }
    }
private:
    // 计算阶乘的实际函数
    int calculateFactorial(int n) {
        int result = 1;
        for (int i = 1; i <= n; i++) {
            result *= i;
        }
        return result;
    }
    // 使用一个 unordered_map 来作为缓存
    unordered_map<int, int> cache_;
};

int main() {
    FactorialCacheProxy cacheProxy;
    for (int i = 5; i <= 7; i++) {
        int result = cacheProxy.getFactorial(i);
        cout << "Factorial of " << i << " is " << result << endl;
    }
    for (int i = 5; i <= 7; i++) {
        int result = cacheProxy.getFactorial(i);
        cout << "Factorial of " << i << " is " << result << endl;
    }
    return 0;
}

        计算结果,并将结果缓存起来。之后再次调用相同的方法时,直接从缓存中取出结果,不需要再进行计算,从而提高了程序的性能。

六、总结

        代理模式被代理的对象一定有奇特的地方,要么对象加载困难,要么计算困难,需要设计一个代理作为缓冲区,在必要时代理才唤醒真正的对象。一切符合上面特性的都可以使用代理模式,设计模式都是自然而然的,都是解决程序运行中的痛点应运而生的。

        博主祝大家周末愉快!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

设计模式之代理模式(Proxy),以C++为例,实现远程代理、虚拟代理、保护代理等。 的相关文章

  • 手把手教你打造自己的4G数传模块

    如何低成本打造自己的4G数传模块 大家好 xff0c 现在给大家介绍一种简单的方法打造自己的4G数传 目录 一 背景 1 二 材料清单 2 2 1 G43模块 2 2 2 PSHAT接口板 5 三 使用方法 5 3 1 硬件连接 6 3 2
  • .error: C++ requires a type specifier for all declarations

    error C 43 43 requires a type specifier for all declarations 出错原因 xff1a 代码片段没有写在函数中 解决方法 xff1a 将代码片段写进函数中
  • 4g图传数传实测

    又一位客户极高的评价 xff0c 为了反馈大家 xff0c 从文章看到的并且加我qq1767893964 xff0c 备注gslink购买者 xff0c 毕淘宝搜索下单的有很大的优惠哦 下面是客户的测试 xff1a 最近南京天气开始变暖 x
  • Unable to locate package错误解决办法

    Ubuntu下执行apt install python pip得到如下错误提示 xff1a Reading package lists Done Building dependency tree Reading state informat
  • 树莓派编译卡死用交换空间问题的解决

    今天在树莓派编译一个ros包时发现 xff0c 每次编译到后速度会特别慢并且卡死 xff0c 经过各种尝试 xff0c 增加2G交换空间后才解决问题 xff0c 编译只花了不到10s xff0c 之前折腾了大半天啊 树莓派3B默认的swap
  • SpringAOP中@EnableAspectJAutoProxy注解的作用

    文章目录 前言从注解开始Import注解封装对象 注入容器 AnnotationAwareAspectJAutoProxyCreator的作用类图回顾IOC对象初始化后置处理器 总结 前言 如果要使用SpringAOP的功能 xff0c 必
  • HDMI转CSI转换板给你做出来了

    小编前段时间一直在做无人机图像的项目 因为项目要求飞机飞行速度较快 小编就像找一款是全局快门 防抖 自动对焦 焦距定焦镜头的一款相机和镜头 首先想到的就是gopro 体积不大 价格也还行 小编之前出去玩一直用对成像效果也是很满意 于是小编就
  • cmakelist.txt 编写教程

    1 CMake编译原理 CMake是一种跨平台编译工具 xff0c 比make更为高级 xff0c 使用起来要方便得多 CMake主要是编写CMakeLists txt文件 xff0c 然后用cmake命令将CMakeLists txt文件
  • VS Code在线安装ESP-IDF出现乱码(已解决)

    VS Code在线安装ESP IDF出现乱码 xff08 已解决 xff09 VS Code安装ESP IDF插件的安装乱码解决办法 VS Code安装 按照乐鑫给出的要求是安装VS Code之前需要安装Git和Python3 xff08
  • ffmpeg推流rtmp指定udp传输

    RTMP Real Time Messaging Protocol 是一个用于音频 视频和数据的传输协议 RTMP 协议本身可以支持 TCP 或 UDP 作为其底层传输协议 在 RTMP 中 xff0c TCP 是默认的传输协议 xff0c
  • 单片机小白学习之路(十五)---定时器和计数器的理解(一)

    目标 xff1a 定时器和计数器的理解 一 1 定时器 计数器简介 定时器 计数器 xff08 Timer Counter xff0c 简称T C xff09 是单片机中最基本的接口之一 即可以定时又可以计数 常用于计数 延时 测量周期 脉
  • stm32---ADXL345

    ADXL345是一款三轴加速度传感器 xff0c 广泛用于手机 游戏手柄等设计 ADXL 支持标准的 I2C 或 SPI 数字接口 xff0c 自带 32 级 FIFO 存储 xff0c 并且内 部有多种运动状态检测和灵活的中断方式等特性
  • fastjson中JSONObject.parse方法使用注意

    今天遇到有同事在使用fastjson的JSONObject时 xff0c 直接在parse方法中传入了一个非json格式的字符串 xff0c 造成有时候报错 xff0c 有时候又能正常返回 问题现象 当你传入一个数值类型时 xff0c 可以
  • HZ和秒之间换算

    Hz和毫秒不能直接换算 xff0c 两者是交流电频率与周期的关系 xff0c 并且是倒数关系 xff1a 周期T 61 1 100 61 0 01秒 61 10毫秒 100Hz即100次 秒 xff0c 即60x100 60秒 xff0c
  • 野火 FireConfig 从SD卡下载镜像到EMMC

    1 用balenaEtcher把镜像下载到SD卡 2 拨码到SD卡启动 3 用MobaXterm当串口终端 xff0c 选择115200 xff0c 取消硬件流 4 输入用户名cat 密码fish 5 输入sudo fire config
  • VCC、VDD、VSS以及VBAT的区别

    原链接 xff1a https blog csdn net LemonLeeB article details 99417945 在STM32 的学习中 xff0c 发现有几种看起来相关的名称 xff0c 分别是VCC VDD VSS VB
  • LWIP_MDNS

    一 xff0e mdns1 什么是mdns xff1f mDNS协议适用于局域网内没有DNS服务器时的域名解析 xff0c 设备通过组播的方式交互DNS记录来完成域名解析 xff0c 约定的组播地址是 xff1a 224 0 0 251 x
  • 组播IGMP

    一 xff0e 什么是组播 xff1f 1 一个发送 组播源 xff0c 多个接收 xff0c 接收的有个特点就是在同一个组播组里面 xff0c 组播组有自己的IP 2 对于组播源来说 xff0c 发送命令到组播IP等于把命令发送到所有组成
  • 单片机小白学习之路(四十三)---LCD12864液晶显示

    目标 xff1a LCD12864原理的理解 1 LCD12864简介 LCD12864可以用来显示字符 数字 汉字 图形等内容 xff0c 其分辨率是128 64点 意思是横着有128个点 xff0c 竖直方向有64点 LCD12864
  • stm32---红外接受

    一个脉冲对应 560us 的连续载波 xff0c 一个逻辑 1 传输需要 2 25ms xff08 560us 脉冲 43 1680us 低电平 xff09 xff0c 一个逻辑 0 的传输需要 1 125ms xff08 560us 脉冲

随机推荐

  • printf重定向

    C语言中printf默认输出设备是显示器 xff0c 当开发板没有时我们就用串口来打印数据 int fputc int ch FILE p USART SendData USART1 ch 如果用串口2打印 xff0c 和换成USART2
  • SPI的CRC校验计算

    22 3 6 CRC计算 CRC校验仅用于保证全双工通信的可靠性 数据发送和数据接收分别使用单独的CRC计算器 通过对每一个接收位进行可编程的多项式运算来计算CRC CRC的计算是在由SPI CR1寄存器 中CPHA和CPOL位定义的采样时
  • 记录JPA并发save时遇到的坑

    前言 在JPA中 xff0c 使用save方法时是这样的 xff1a 如果我们save的对象指定了主键 xff0c 那么会根据主键先进行一次查询 xff0c 如果查询记录不存在则执行insert语句 xff0c 如果查询记录存在则执行upd
  • Openmv(一)OpenMV图像处理的基本方法

    一 图像处理基础知识 摄像头 xff1a 光学信号转换成电信号 计算机视觉中 xff0c 最简单的模型是小孔成像模型 小孔成像是一种理想模型 xff0c 实际镜头会存在场曲和畸变等 xff0c 但可以通过在标定过程中引入畸变参数解决 xff
  • CMakeLists详解

    CMakeLists详解 一 CMake简介 cmake 是一个跨平台 开源的构建系统 它是一个集软件构建 测试 打包于一身的软件 它使用与平台和编译器独立的配置文件来对软件编译过程进行控制 二 常用命令 1 指定cmake最小版本 cma
  • c++继承与多态总结

    不知不觉C 43 43 课程的学习已经接近尾声 xff0c 感觉自己对于c 43 43 的认知更近了一步 xff0c 粗略总结一下最近学习的继承与多态部分的知识 继承 C 43 43 的继承 继承有3种形式 xff1a 私有继承 保护继承
  • C++对象的销毁

    对象的销毁 一般来说 xff0c 需要销毁的对象都应该做清理 解决方案 1 为每个类都提供一个public的free函数 xff1b 2 对象不再需要时立即调用free函数进行清理 析构函数 1 C 43 43 的类中可以定义一个特殊的清理
  • C++中类中的函数重载

    类中的函数重载 函数重载的回顾 1 函数重载的本质就是为相互独立的不同函数 xff1b 2 C 43 43 中通过函数名和函数参数确定函数调用 xff1b 3 无法直接通过函数名得到重载函数的入口地址 xff1b 4 函数重载必然发生在同一
  • C++中的字符串类

    字符串类 历史遗留的问题 1 C语言不支持真正意义上的字符串 xff1b 2 C语言用字符数组和一组实现字符串操作 xff1b 3 C语言不支持自定义类型 xff0c 因此无法获得字符类型 xff1b 解决方案 1 从C到C 43 43 的
  • MySQL中的Block Nested Loop优化分析

    前言 一般在MySQL规范中 xff0c 都会规定如果两张表进行join查询 xff0c 那么join的字段一定要有索引 xff0c 在之前的文章中我们分析了MySQL join大小表前后顺序影响分析 xff0c 这是在有索引的情况下 xf
  • C++之类模板的概念和意义

    类模板 一些类主要用于存储和组织数据元素 类中数据组织的方式和数据元素的具体类型无关 如 xff1a 数组类 链表类 Stack Queue类 等 1 C 43 43 中将模板的思想应用于类 xff0c 使得类的实现不关注数据元素的具体类型
  • C++之单例类模板

    需求的提出 在架构设计时 xff0c 某些类在整个系统生命周期中最多只能有一个对象存在 xff08 Single Instance xff09 要控制类的对象数目 xff0c 必须对外隐藏构造函数 xff1b 思路 xff1a 1 将构造函
  • 【无标题】

    绘图控件GraphicsView 一 GraphicsView简介 1 QT有多种绘图相关的技术 xff0c 我们将在第2部分 2 4 QT绘图和图表 中比较详细系统的讲 2 本节简单讲一下GraphicsView的基本理论 xff0c 并
  • uboot源码分析之start.S解析

    1 start S引入 1 1 u boot lds中找到start S入口 1 在uboot中因为有汇编阶段参与 xff0c 因此不能直接找main c 整个程序的入口取决于链接脚本中ENTRY声明的地方 ENTRY start 因此 s
  • uboot启动第二阶段

    uboot启动第二阶段 start armboot函数简介 一个很长的函数 1 这个函数在uboot lib arm board c的第444行开始到908行结束 2 450行还不是全部 xff0c 因为里面还调用了别的函数 3 为什么这么
  • cmake设置编译类型为release命令

    cmake编译类型通常默认为debug xff0c 但是在编译软件时 xff0c 一般都需要使用release版本的 xff0c debug太慢了 设置为release版本可以在cmake文件里进行 xff0c 也可以在运行cmake命令时
  • 设计模式之单例模式(Singleton),以C++为例,实现日志输出。

    Hello大家好 xff0c 好久没更新了 xff0c 今天给大家补上最基础的设计模式 xff1a 单例模式 这个单例模式实在是我的心结啊 xff0c 2021年末左右面试京东算法岗 xff0c 面试官让我写一个单例 xff0c 没写出来
  • 源码分析MyBatis对数值(int、double)类型进行test判断的误区

    文章目录 问题描述问题分析验证解析表达式执行解析后表达式分别测试两个条件 查询Ognl官方文档验证问题解决 问题描述 在如下判断中 xff0c 如果type类型为int xff0c 那么对于type 61 39 39 部分判断会出现一些问题
  • Git报错:error: xxxx bytes of body are still expected.

    git一个很老的项目 xff0c 项目深度很深 xff0c 报错 xff1a error 7857 bytes of body are still expected fetch pack unexpected disconnect whil
  • 设计模式之代理模式(Proxy),以C++为例,实现远程代理、虚拟代理、保护代理等。

    兄弟姐妹们好 xff0c 又是好久没有更新了 xff0c 今天给大家简单介绍代理模式 xff0c 一个很简单的设计模式 xff0c 旨在不改变原对象的情况下通过代理对象来控制对原对象的访问 代理模式根据具体情况还可以分为远程代理 虚拟代理