C++设计模式:代理模式(Proxy)(附案例代码)

什么是代理模式?

代理模式是一种结构型设计模式,主要用于为某个对象提供一个代理,以便在不直接访问对象的情况下控制对其的访问。代理可以在客户端和目标对象之间起到一个中介的作用,添加一些额外的操作,例如权限控制、性能优化(如延迟加载)、结果缓存、日志记录等。

通过代理模式,客户端与目标对象之间的耦合被降低,同时实现了对目标对象的透明化访问。


代理模式的特点

优点

  1. 控制对象访问

    • 可以通过代理控制对目标对象的访问,比如权限检查或延迟加载。
  2. 解耦增强系统灵活性

    • 客户端只需与代理对象交互,代理负责实际的操作逻辑,隐藏了具体的实现细节。
  3. 额外功能增强

    • 在不修改目标对象代码的前提下,通过代理扩展功能,例如添加日志记录、性能优化等。

缺点

  1. 增加系统复杂性

    • 引入代理对象会增加代码量和复杂性,尤其是代理逻辑较为复杂时。
  2. 性能开销

    • 代理本身需要额外的内存和处理时间。

代理模式的使用场景

  1. 权限控制(保护代理)

    • 根据用户角色或权限控制对目标对象的访问。
    • 例如:管理员可以查看银行账户余额,而普通用户不能。
  2. 延迟加载(虚拟代理)

    • 在需要时才创建目标对象。
    • 例如:大型资源(如图像、文件)在实际使用时再加载。
  3. 远程代理(Remote Proxy)

    • 为位于不同地址空间的对象提供本地代理。
    • 例如:远程方法调用(RMI)。
  4. 日志记录或审计

    • 在目标对象操作前后添加日志记录。
  5. 性能优化(缓存代理)

    • 缓存目标对象的操作结果,避免重复计算或网络请求。
  6. 智能引用(Smart Reference Proxy)

    • 在访问目标对象时进行计数、引用管理或预处理。

案例详解

以下通过两个案例详细解读代理模式:

  1. 虚拟代理(延迟加载)
  2. 保护代理(权限控制)

案例一:虚拟代理(延迟加载)

需求背景

假设我们要开发一个图片查看器应用。加载高清图片非常耗时,为了提升应用性能,我们可以使用代理模式,在需要时才加载图片。

代码实现
  1. 定义抽象接口

首先定义一个 Image 接口,供代理对象和目标对象实现。

#include <iostream>
#include <string>
#include <memory>
using namespace std;// 抽象图片接口
class Image {
public:virtual void display() = 0; // 定义显示图片的接口virtual ~Image() = default;
};

  1. 实现真实对象

RealImage 是需要加载的高清图片,它模拟了一个耗时的加载过程。

class RealImage : public Image {
private:string fileName;void loadFromDisk() {// 模拟加载图片cout << "Loading image: " << fileName << endl;}public:RealImage(const string& fileName) : fileName(fileName) {loadFromDisk(); // 在创建时加载图片}void display() override {cout << "Displaying image: " << fileName << endl;}
};

  1. 实现代理对象

ProxyImage 是虚拟代理,只有在需要显示图片时,才创建 RealImage

class ProxyImage : public Image {
private:string fileName;unique_ptr<RealImage> realImage; // 延迟加载的真实图片对象public:ProxyImage(const string& fileName) : fileName(fileName) {}void display() override {if (!realImage) {// 延迟加载真实图片realImage = make_unique<RealImage>(fileName);}realImage->display();}
};

  1. 客户端代码

客户端通过代理对象显示图片。

int main() {// 创建代理图片unique_ptr<Image> image = make_unique<ProxyImage>("test.jpg");// 第一次显示图片cout << "First time displaying image:" << endl;image->display();// 第二次显示图片cout << "Second time displaying image:" << endl;image->display();return 0;
}

运行结果
First time displaying image:
Loading image: test.jpg
Displaying image: test.jpg
Second time displaying image:
Displaying image: test.jpg
解读
  • 第一次显示时,ProxyImage 延迟加载了 RealImage,并执行加载操作。
  • 第二次显示时,直接使用已经加载好的图片,提升了性能。

案例二:保护代理(权限控制)

需求背景

实现一个银行账户系统,只有管理员用户可以查看账户余额,普通用户尝试查看时会被拒绝。

代码实现
  1. 定义接口

定义 BankAccount 接口,提供查看余额的功能。

class BankAccount {
public:virtual void showBalance() = 0; // 查看余额方法virtual ~BankAccount() = default;
};

  1. 实现真实对象

RealBankAccount 存储账户余额,并提供查看功能。

class RealBankAccount : public BankAccount {
private:double balance;public:RealBankAccount(double initialBalance) : balance(initialBalance) {}void showBalance() override {cout << "The balance is: $" << balance << endl;}
};

  1. 实现代理对象

BankAccountProxy 负责检查用户角色,只有管理员可以访问 RealBankAccount

class BankAccountProxy : public BankAccount {
private:unique_ptr<RealBankAccount> realAccount;string role; // 用户角色public:BankAccountProxy(double initialBalance, const string& userRole): realAccount(make_unique<RealBankAccount>(initialBalance)), role(userRole) {}void showBalance() override {if (role == "admin") {realAccount->showBalance();} else {cout << "Access denied: Only admin can view the balance." << endl;}}
};

  1. 客户端代码

客户端通过代理访问账户信息。

int main() {// 普通用户unique_ptr<BankAccount> userAccount = make_unique<BankAccountProxy>(1000.0, "user");userAccount->showBalance();// 管理员unique_ptr<BankAccount> adminAccount = make_unique<BankAccountProxy>(1000.0, "admin");adminAccount->showBalance();return 0;
}

运行结果
Access denied: Only admin can view the balance.
The balance is: $1000
解读
  • 普通用户尝试查看余额时,代理拒绝访问。
  • 管理员可以通过代理访问真实账户信息。

完整代码项目

文件结构
proxy_pattern_project/
│
├── main.cpp       // 主程序入口
├── Image.h        // 图片接口及相关类
├── BankAccount.h  // 银行账户接口及相关类

1. Image.h

// Image.h
#ifndef IMAGE_H
#define IMAGE_H#include <iostream>
#include <string>
#include <memory>
using namespace std;// 抽象图片接口
class Image {
public:virtual void display() = 0; // 定义显示图片的接口virtual ~Image() = default;
};// 真实图片类
class RealImage : public Image {
private:string fileName;void loadFromDisk() {// 模拟加载图片的过程cout << "加载图片:" << fileName << endl;}public:RealImage(const string& fileName) : fileName(fileName) {loadFromDisk(); // 构造时加载图片}void display() override {cout << "显示图片:" << fileName << endl;}
};// 代理图片类
class ProxyImage : public Image {
private:string fileName;unique_ptr<RealImage> realImage; // 延迟加载的真实图片对象public:ProxyImage(const string& fileName) : fileName(fileName) {}void display() override {if (!realImage) {// 延迟加载真实图片realImage = make_unique<RealImage>(fileName);}realImage->display();}
};#endif // IMAGE_H

2. BankAccount.h

// BankAccount.h
#ifndef BANK_ACCOUNT_H
#define BANK_ACCOUNT_H#include <iostream>
#include <memory>
#include <string>
using namespace std;// 抽象银行账户接口
class BankAccount {
public:virtual void showBalance() = 0; // 查看余额的方法virtual ~BankAccount() = default;
};// 真实银行账户类
class RealBankAccount : public BankAccount {
private:double balance;public:RealBankAccount(double initialBalance) : balance(initialBalance) {}void showBalance() override {cout << "账户余额:$" << balance << endl;}
};// 银行账户代理类
class BankAccountProxy : public BankAccount {
private:unique_ptr<RealBankAccount> realAccount;string role; // 用户角色public:BankAccountProxy(double initialBalance, const string& userRole): realAccount(make_unique<RealBankAccount>(initialBalance)), role(userRole) {}void showBalance() override {if (role == "admin") {realAccount->showBalance();} else {cout << "访问被拒绝:只有管理员可以查看余额。" << endl;}}
};#endif // BANK_ACCOUNT_H

3. main.cpp

// main.cpp
#include "Image.h"
#include "BankAccount.h"int main() {cout << "====== 虚拟代理示例(延迟加载图片) ======" << endl;// 创建代理图片对象unique_ptr<Image> image = make_unique<ProxyImage>("test.jpg");// 第一次显示图片(触发加载)cout << "第一次显示图片:" << endl;image->display();// 第二次显示图片(直接显示已加载的图片)cout << "第二次显示图片:" << endl;image->display();cout << "\n====== 保护代理示例(银行账户访问控制) ======" << endl;// 普通用户访问unique_ptr<BankAccount> userAccount = make_unique<BankAccountProxy>(1000.0, "user");cout << "普通用户尝试查看余额:" << endl;userAccount->showBalance();// 管理员访问unique_ptr<BankAccount> adminAccount = make_unique<BankAccountProxy>(1000.0, "admin");cout << "管理员查看余额:" << endl;adminAccount->showBalance();return 0;
}

示例输出

虚拟代理示例

====== 虚拟代理示例(延迟加载图片) ======
第一次显示图片:
加载图片:test.jpg
显示图片:test.jpg
第二次显示图片:
显示图片:test.jpg

保护代理示例

====== 保护代理示例(银行账户访问控制) ======
普通用户尝试查看余额:
访问被拒绝:只有管理员可以查看余额。
管理员查看余额:
账户余额:$1000

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【笔记】软技能

硬技能&#xff1a;操控世界的能力&#xff0c;处理对象为【物】。软技能&#xff1a;影响他人的能力&#xff0c;处理对象为【人】。软技能包括一个人的情商、个性、社交礼仪、沟通、语言、个人习惯&#xff0c;还有解决问题的能力、领导能力、时间管理能力等一切非技术能力。…

uni-app简洁的移动端登录注册界面

非常简洁的登录、注册界面模板&#xff0c;使用uni-app编写&#xff0c;直接复制粘贴即可&#xff0c;无任何引用&#xff0c;全部公开。 废话不多说&#xff0c;代码如下&#xff1a; login.vue文件 <template><view class"content"><view class&quo…

单台服务器上创建多个端口MySQL服务

单台服务器上创建多个端口MySQL服务 直接拷贝已经运行的数据库文件: # ll /data/mysql/ 总用量 204 drwxr-x--- 2 mysql mysql 4096 9月 15 2023 bin -rw-r--r-- 1 mysql mysql

【开源免费】基于SpringBoot+Vue.JS购物推荐网站(JAVA毕业设计)

博主说明&#xff1a;本文项目编号 T 073 &#xff0c;文末自助获取源码 \color{red}{T073&#xff0c;文末自助获取源码} T073&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析…

AI在SEO中的应用与关键词优化探讨

内容概要 在当今数字化时代&#xff0c;人工智能&#xff08;AI&#xff09;技术为搜索引擎优化&#xff08;SEO&#xff09;带来了革命性的改变。传统的SEO主要依赖于人为的经验和判断&#xff0c;而AI则通过算法分析海量数据&#xff0c;提供更加精准和高效的方式优化关键词…

Tomcat新手成长之路:安装部署优化全解析(下)

接上篇《Tomcat新手成长之路&#xff1a;安装部署优化全解析&#xff08;上&#xff09;》: link 文章目录 7.应用部署7.1.上下文7.2.启动时进行部署7.3.动态应用部署 8.Tomcat 类加载机制8.1.简介8.2.类加载器定义8.3.XML解析器和 Java 9.JMS监控9.1.简介9.2.启用 JMX 远程监…

服务器数据恢复—服务器raid0阵列硬盘指示灯显示黄颜色的数据恢复案例

服务器数据恢复环境&故障情况&#xff1a; 某品牌服务器上有一组由两块SAS硬盘组建的raid0阵列&#xff0c;上层是windows server操作系统ntfs文件系统。服务器上一个硬盘指示灯显示黄颜色&#xff0c;该指示灯对应的硬盘离线&#xff0c;raid不可用。 服务器数据恢复过程…

Ant-Design-Vue 全屏下拉日期框无法显示,能显示后小屏又位置错乱

问题1&#xff1a;在全屏后 日期选择器的下拉框无法显示。 解决&#xff1a;在Ant-Design-Vue的文档中&#xff0c;很多含下拉框的组件都有一个属性 getPopupContainer可以用来指定弹出层的挂载节点。 在该组件上加上 getPopupContainer 属性,给挂载到最外层盒子上。 <temp…

php 系统函数 记录

PHP intval() 函数 PHP函数介绍—array_key_exists(): 检查数组中是否存在特定键名 如何使用PHP中的parse_url函数解析URL PHP is_array()函数详解&#xff0c;PHP判断是否为数组 PHP函数介绍&#xff1a;in_array()函数 strpos定义和用法 strpos() 函数查找字符串在另一字符串…

Hive学习基本概念

基本概念 hive是什么&#xff1f; Facebook 开源&#xff0c;用于解决海量结构化日志的数据统计。 基于Hadoop的一个数据仓库工具&#xff0c;可以将结构化的数据文件映射为一张表&#xff0c;并提供类SQL查询功能 本质是将HQL转化为MapReduce程序。 Hive处理的数据存储在H…

chrome使用问题记录

1. http自动跳转https问题 step1. 地址栏输入&#xff1a; chrome://net-internals/#hsts step2. 找到底部Delete domain security policies一栏&#xff0c;输入想处理的域名&#xff0c;点击delete。 注意&#xff1a;输入域名时去掉前缀http step3. 搞定了&#xff0c;再…

内网穿透 natapp安装与使用

前言 NATAPP是一款基于ngrok的内网穿透工具。以下是对NATAPP的详细概述&#xff1a; 基本概念 定义&#xff1a;内网穿透&#xff08;NAT穿透&#xff09;是一种技术&#xff0c;它允许具有特定源IP地址和端口号的数据包能够绕过NAT设备&#xff0c;从而被正确地路由到内网主机…

计算机光电成像理论基础

一、透过散射介质成像 1.1 光在散射介质中传输 光子携带物体信息并进行成像的过程是一个涉及光与物质相互作用的物理现象。这个过程可以分为几个步骤来理解&#xff1a; 1. **光的发射或反射**&#xff1a; - 自然界中的物体可以发射光&#xff08;如太阳&#xff09;&am…

视频监控汇聚平台Liveweb视频安防监控实时视频监控系统操作方案

Liveweb国标GB28181视频平台是一种基于国标GB/T28181协议的安防视频流媒体能力平台。它支持多种视频功能&#xff0c;包括实时监控直播、录像、检索与回看、语音对讲、云存储、告警以及平台级联等功能。该平台部署简单、可扩展性强&#xff0c;支持全终端、全平台分发接入的视频…

ASP.NET Core 9.0 静态资产传递优化 (MapStaticAssets )

一、结论 &#x1f4a2;先看结论吧&#xff0c; MapStaticAssets 在大多数情况下可以替换 UseStaticFiles&#xff0c;它已针对为应用在生成和发布时了解的资产提供服务进行了优化。 如果应用服务来自其他位置&#xff08;如磁盘或嵌入资源&#xff09;的资产&#xff0c;则应…

在 Windows 11 WSL (Ubuntu 24.04.1 LTS) | Python 3.12.x 下部署密码学库 charm

1. 在 Windows 11 上部署 Ubuntu (WSL) 由于作者没有高性能的 Ubuntu 服务器或个人电脑&#xff0c;且公司或学校提供的 Ubuntu 服务器虽然提供高性能 GPU 等硬件配置但通常不会提供 root 权限&#xff0c;因而作者通过在搭载了 Windows 11 的个人电脑上启动 Ubuntu (WSL) 来进…

element-ui radio和checkbox禁用时不置灰还是原来不禁用时的样式

把要紧用的内容加上一个class"notEdit-page" z注意要在style里面写不能加上scoped /*//checkBox自定义禁用样式*//*//checkBox自定义禁用样式*/ .notEdit-page.el-checkbox__input.is-disabled.is-checked.el-checkbox__inner::after {border-color: #fff; } .notEdi…

Qt自定义 Qt Designer 插件

创建 Qt Designer 插件项目 Qt 提供两种设计插件的 API&#xff0c;可以用于扩展 Qt 的功能。高级 API 用于设计插件以扩展 Qt 的功能&#xff0c;例如定制数据库驱动、图像格式、文本编码、定制样式等。Qt Designer 里大量采用了插件&#xff0c;点击 Qt Creator 的“Help”-…

springboot利用easypoi实现简单导出Excel

vue springboot利用easypoi实现简单导出 前言一、easypoi是什么&#xff1f;二、使用步骤 1.传送门2.前端vue3.后端springboot 3.1编写实体类&#xff08;我这里是dto,也一样&#xff09;3.2控制层结尾 前言 今天玩了一下springboot利用easypoi实现excel的导出&#xff0c;以前…

【学习总结|DAY012】Java面向对象基础

一、前言 今天主要学习了以下内容&#xff1a;面向对象的理解与使用、对象的内存布局、构造器的概念和作用、封装的重要性以及JavaBean实体类的实现等。下面我将详细阐述这些知识点。 二、面向对象的理解与使用 1. 什么是面向对象&#xff1f; 类&#xff1a;一种特殊的数据…