当前位置: 首页 > java >正文

设计模式:代理模式(Proxy Pattern)

文章目录

    • 一、代理模式的定义
    • 二、实例分析
    • 三、示例代码

一、代理模式的定义

  代理模式是一种结构型设计模式,它为某个对象提供一个代理或占位符,以控制对这个对象的访问。简单来说代理对象在客户端和目标对象之间起到中介作用,客户端并不会直接操作目标对象,而是通过代理对象间接访问。
在这里插入图片描述
代理模式常用于以下情况:

  1. 远程代理:为远程对象(在不同地址空间、服务器上)提供本地代理,隐藏远程访问的复杂性。
  2. 虚拟代理:在需要消耗大量资源时,先用代理延迟对象的创建或初始化。(如:大图片的延迟加载)
  3. 保护代理:控制不同用户对对象的访问权限。(如:文件系统访问控制)
  4. 智能代理:在访问对象时附加额外操作(如缓存、日志、计数、线程安全控制)。

代理模式结构:
在这里插入图片描述

二、实例分析

问题:
为什么要控制对于某个对象的访问呢? 举个例子: 有这样一个消耗大量系统资源的巨型对象, 你只是偶尔需要使用它, 并非总是需要。
在这里插入图片描述
你可以实现延迟初始化: 在实际有需要时再创建该对象。 对象的所有客户端都要执行延迟初始代码。 不幸的是, 这很可能会带来很多重复代码。

在理想情况下, 我们希望将代码直接放入对象的类中, 但这并非总是能实现: 比如类可能是第三方封闭库的一部分。

解决方案:
代理模式建议新建一个与原服务对象接口相同的代理类, 然后更新应用以将代理对象传递给所有原始对象客户端。 代理类接收到客户端请求后会创建实际的服务对象, 并将所有工作委派给它。
在这里插入图片描述
这有什么好处呢? 如果需要在类的主要业务逻辑前后执行一些工作, 你无需修改类就能完成这项工作。 由于代理实现的接口与原类相同, 因此你可将其传递给任何一个使用实际服务对象的客户端。

真实世界类比:
在这里插入图片描述
信用卡是银行账户的代理, 银行账户则是一大捆现金的代理。 它们都实现了同样的接口, 均可用于进行支付。 消费者会非常满意, 因为不必随身携带大量现金; 商店老板同样会十分高兴, 因为交易收入能以电子化的方式进入商店的银行账户中, 无需担心存款时出现现金丢失或被抢劫的情况。

代码实现:

  • 抽象接口:Payment,定义统一的 pay(amount) 方法。
  • 真实主题(RealSubject):Cash,表示真正的支付方式(现金支付)
  • 代理(Proxy):CreditCard,作为银行账户的代理,内部持有一个 BankAccount(或 Cash)对象,通过代理实现支付。
#include <iostream>
#include <memory>
using namespace std;// 抽象接口
class Payment {
public:virtual void pay(double amount) = 0;virtual ~Payment() = default;
};// 真实主题:现金支付
class Cash : public Payment {
public:void pay(double amount) override {cout << "支付现金: " << amount << " 元" << endl;}
};// 银行账户(被代理对象)
class BankAccount {
public:void withdraw(double amount) {cout << "从银行账户扣款: " << amount << " 元" << endl;}
};// 代理:信用卡支付
class CreditCard : public Payment {
private:BankAccount* account; // 代理对象内部持有真实对象
public:CreditCard(BankAccount* acc) : account(acc) {}void pay(double amount) override {cout << "使用信用卡支付: " << amount << " 元" << endl;account->withdraw(amount); // 实际通过银行账户扣款}
};// 客户端
int main() {// 使用现金支付unique_ptr<Payment> cashPayment = make_unique<Cash>();cashPayment->pay(100);// 使用信用卡支付(代理)BankAccount bankAcc;unique_ptr<Payment> creditPayment = make_unique<CreditCard>(&bankAcc);creditPayment->pay(200);return 0;
}

三、示例代码

示例一:

#include <iostream>
#include <memory>
using namespace std;// 抽象主题
class Subject {
public:virtual void request() = 0;virtual ~Subject() = default;
};// 真实主题
class RealSubject : public Subject {
public:void request() override {cout << "RealSubject: Handling request." << endl;}
};// 代理类
class Proxy : public Subject {
private:unique_ptr<RealSubject> realSubject;
public:void request() override {if (!realSubject) {cout << "Proxy: Creating RealSubject." << endl;realSubject = make_unique<RealSubject>();}cout << "Proxy: Delegating request to RealSubject." << endl;realSubject->request();}
};// 客户端
int main() {Proxy proxy;proxy.request(); // 第一次访问,创建真实对象proxy.request(); // 第二次访问,直接使用已有对象return 0;
}

示例二:
数据库模块(QSqlQuery)+ 缓存代理的实际应用示例

设计思路:

  • 接口类 IDatabase:定义通用的查询接口
  • 真实类 RealDatabase:直接使用 QSqlDatabase + QSqlQuery 访问数据库。
  • 代理类 DatabaseProxy:在真实查询前先检查缓存,缓存命中就直接返回结果,避免重复访问数据库。
#include <QCoreApplication>
#include <QtSql/QSqlDatabase>
#include <QtSql/QSqlQuery>
#include <QtSql/QSqlError>
#include <QDebug>
#include <QVariant>
#include <QMap>
#include <QStringList>// 抽象接口
class IDatabase {
public:virtual QStringList query(const QString& sql) = 0;virtual ~IDatabase() = default;
};// 真实数据库类
class RealDatabase : public IDatabase {
private:QSqlDatabase db;
public:RealDatabase() {// 连接 SQLite 数据库db = QSqlDatabase::addDatabase("QSQLITE");db.setDatabaseName(":memory:"); // 内存数据库,演示用if (!db.open()) {qWarning() << "Database error:" << db.lastError().text();}// 初始化数据QSqlQuery q;q.exec("CREATE TABLE users (id INT, name TEXT)");q.exec("INSERT INTO users VALUES (1, 'Alice')");q.exec("INSERT INTO users VALUES (2, 'Bob')");}QStringList query(const QString& sql) override {QSqlQuery q;QStringList result;if (!q.exec(sql)) {qWarning() << "SQL error:" << q.lastError().text();return result;}while (q.next()) {result << q.value(0).toString(); // 只取第一列}qDebug() << "[RealDatabase] Executing:" << sql;return result;}
};// 代理类,带缓存
class DatabaseProxy : public IDatabase {
private:RealDatabase* realDb;QMap<QString, QStringList> cache; // SQL -> 查询结果
public:DatabaseProxy() : realDb(nullptr) {}QStringList query(const QString& sql) override {// 缓存检查if (cache.contains(sql)) {qDebug() << "[Proxy] Cache hit for:" << sql;return cache[sql];}// 创建真实对象if (!realDb) {realDb = new RealDatabase();}qDebug() << "[Proxy] Cache miss, querying DB:" << sql;QStringList result = realDb->query(sql);// 存入缓存cache[sql] = result;return result;}~DatabaseProxy() {delete realDb;}
};// 测试
int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);IDatabase* db = new DatabaseProxy();qDebug() << db->query("SELECT name FROM users WHERE id=1");qDebug() << db->query("SELECT name FROM users WHERE id=2");qDebug() << db->query("SELECT name FROM users WHERE id=1"); // 命中缓存delete db;return 0;
}
http://www.xdnf.cn/news/19526.html

相关文章:

  • 有N个控制点的三次B样条曲线转化为多段三阶Bezier曲线的方法
  • 【开题答辩全过程】以 基于微信小程序的校园二手物品交易平台的设计与实现为例,包含答辩的问题和答案
  • 8K4K图像评估平台
  • 【系统架构设计(七)】 需求工程之:面向对象需求分析方法:统一建模语言(UML)(下)
  • 像信号处理一样理解中断:STM32与RK3399中断机制对比及 Linux 驱动开发实战
  • 数组(4)
  • QMainWindow使用QTabWidget添加多个QWidget
  • 【数学建模学习笔记】数据标准化
  • LeetCode刷题记录----74.搜索二维矩阵(Medium)
  • 构建无广告私人图书馆Reader与cpolar让电子书库随身携带
  • 站在巨人的肩膀上:gRPC通过HTTP/2构建云原生时代的通信标准
  • Unity游戏打包——打包流程
  • 【C++】类型转换详解:显式与隐式转换的艺术
  • Vue2存量项目国际化改造踩坑
  • Ansible变量的定义与使用
  • 安卓11 12系统修改定制化_____常用的几种修改固件 实现指定 “运行内存” 显示
  • 【lucene】 中的impactsenum与impactsdisi有啥区别?
  • 拥抱智能高效翻译 ——8 款视频翻译工具深度测评
  • (附源码)留言系统的设计与实现
  • 标定分享3--lidar与rtk/ins标定外参工程实现分享
  • 变频器实习总结14 电子元件中的内部参考电压 Type-c口对于BMS开发的优点
  • Synchronized 概述
  • 平衡二叉树(一)
  • 2016考研数学(二)真题
  • sunset: noontide靶场
  • AlphaFold 2 本地部署与安装教程(Linux)
  • 高速CANFD通讯接口芯片ASM1042性能分析与5Mbps多节点测验
  • 包的相对导入
  • MPI-NCCL-TEST 训练自检,基础通信和可用的机器
  • 《Bishop PRML》10.1 (3) 理解VAE KL loss