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

设计模式:代理模式(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.dtcms.com/a/362009.html

相关文章:

  • HOW - 前端团队组长提升(沟通篇)
  • kubectl-etcd
  • RSA的CTF题目环境和做题复现第1集
  • nacos微服务介绍及环境搭建
  • linux 函数 kthread_should_stop
  • 「Unity3D」RectTransform的AnchorMin与Max直接定位到Rect四角,使得Left-Top-Right-Bottom都为0
  • 【Spark Core】(三)RDD的持久化
  • MT-Workflow: Odoo 可视化工作流引擎
  • 程序员与杀毒软件:一场不必要的“战争”?程序员用什么杀毒软件?-优雅草卓伊凡
  • 人工智能语音交互开发(五)麦克风频率响应范围
  • PostgreSQL 索引大全
  • 奇偶破题:当反函数撞上奇函数
  • Nano Banana一战封神,我总结了10种官方不会告诉你的神级技巧。
  • 太浅显数学常识暴露太重大数学真相:同样是有首项的无穷数列,此列的项可多于彼列的项
  • 使用修改过的arj源码编译和测试
  • java中常见的几种排序算法
  • 算法练习——189.轮转数组
  • 二、SVN基础命令速查表
  • Python OpenCV图像处理与深度学习:Python OpenCV对象检测入门-Haar级联分类器与人脸检测
  • LeetCode刷题-top100( 除自身以外数组的乘积)
  • MATLAB 实现同步压缩小波变换
  • C++ 虚继承:破解菱形继承的“双亲困境”
  • 拆解期货交易所:清算交收体系!
  • 配置sscms时被sql server处处刁难
  • 从 Skip Take 到 Keyset:C# 分页原理与实践
  • 考研复习-计算机网络-第一章-计算机网络概述
  • Parasoft C/C++test解析:覆盖率与可追溯性的软件测试协同方案
  • 免费GIS服务器方案:OGC标准3DTiles服务发布与跨平台渲染实践
  • 安全等保复习笔记
  • flume接收处理器:构建高可用与高性能的数据链路