C++设计模式之结构型模式:代理模式(Proxy)

代理模式(Proxy)是结构型设计模式的一种,它通过提供一个替代对象来控制对原对象的访问,常用于在不改变原对象的前提下,为其添加额外功能(如权限控制、延迟加载、日志记录等)。这种模式类似于现实生活中的"代理人"或"中介",客户端通过代理与目标对象交互。

一、核心思想与角色

代理模式的核心是"控制访问,增强功能",通过引入代理对象,在客户端与目标对象之间建立一层中间层。其核心角色如下:

角色名称 核心职责
抽象主题(Subject) 定义目标对象和代理对象的共同接口,确保代理可替代目标对象。
真实主题(RealSubject) 实现抽象主题接口,是被代理的真实对象,包含核心业务逻辑。
代理(Proxy) 实现抽象主题接口,内部持有真实主题的引用,在调用真实主题方法前后添加额外逻辑(如权限校验)。
客户端(Client) 通过抽象主题接口与代理交互,无需知道代理背后是否是真实对象。

核心思想:代理与真实对象实现相同接口,客户端无需区分两者;代理在转发请求的同时,可添加额外操作,实现对真实对象的访问控制和功能增强。

二、实现示例(图片加载代理)

假设我们需要加载大型图片(如高清图片),直接加载可能消耗大量资源且耗时。使用代理模式可实现"延迟加载"(仅在需要显示时才真正加载图片),并添加日志记录功能:

cpp 复制代码
#include <iostream>
#include <string>

// 1. 抽象主题(Subject):图片
class Image {
public:
    virtual void display() const = 0; // 显示图片
    virtual ~Image() = default;
};

// 2. 真实主题(RealSubject):高清图片
class HighResolutionImage : public Image {
private:
    std::string filename; // 图片文件名

    // 真实加载图片(耗时操作)
    void loadFromDisk() const {
        std::cout << "从磁盘加载高清图片:" << filename << std::endl;
    }

public:
    HighResolutionImage(const std::string& name) : filename(name) {
        // 构造函数不加载图片(延迟到display时)
    }

    // 显示图片(实际加载并显示)
    void display() const override {
        loadFromDisk(); // 真实加载
        std::cout << "显示高清图片:" << filename << std::endl;
    }
};

// 3. 代理(Proxy):图片代理
class ImageProxy : public Image {
private:
    HighResolutionImage* realImage; // 真实图片对象(延迟初始化)
    std::string filename;           // 图片文件名
    std::string userRole;           // 用户角色(用于权限控制)

    // 检查权限
    bool hasPermission() const {
        return userRole == "admin" || userRole == "viewer";
    }

public:
    // 构造函数:仅存储文件名和用户角色,不创建真实对象
    ImageProxy(const std::string& name, const std::string& role)
        : realImage(nullptr), filename(name), userRole(role) {}

    // 显示图片:代理控制逻辑
    void display() const override {
        // 1. 权限校验
        if (!hasPermission()) {
            std::cout << "权限不足:用户" << userRole << "无法查看图片" << std::endl;
            return;
        }

        // 2. 延迟加载:仅在需要时创建真实对象
        if (!realImage) {
            realImage = new HighResolutionImage(filename);
            std::cout << "代理:创建真实图片对象" << std::endl;
        }

        // 3. 日志记录
        std::cout << "代理:记录图片访问 - " << filename << std::endl;

        // 4. 调用真实对象的方法
        realImage->display();
    }

    // 析构函数:释放真实对象
    ~ImageProxy() override {
        delete realImage;
    }
};

// 客户端代码:通过代理访问图片
int main() {
    // 1. 无权限用户访问
    Image* proxy1 = new ImageProxy("vacation.jpg", "guest");
    std::cout << "=== 游客访问 ===" << std::endl;
    proxy1->display();

    // 2. 有权限用户访问(首次加载)
    Image* proxy2 = new ImageProxy("vacation.jpg", "viewer");
    std::cout << "\n=== 查看者首次访问 ===" << std::endl;
    proxy2->display();

    // 3. 有权限用户再次访问(复用真实对象)
    std::cout << "\n=== 查看者再次访问 ===" << std::endl;
    proxy2->display();

    // 释放资源
    delete proxy1;
    delete proxy2;

    return 0;
}

三、代码解析

  1. 抽象主题(Image) :定义了display()接口,是真实图片和代理的共同基类,确保代理可替代真实对象。

  2. 真实主题(HighResolutionImage) :实现了高清图片的核心功能,loadFromDisk()是耗时操作(模拟大型图片加载),display()负责实际显示。

  3. 代理(ImageProxy)

    • 权限控制hasPermission()检查用户是否有权限访问图片,无权限则拒绝请求。
    • 延迟加载 :仅在首次调用display()且有权限时,才创建HighResolutionImage对象,避免不必要的资源消耗。
    • 日志记录:在调用真实对象方法前记录访问日志。
    • 转发请求 :最终调用真实对象的display()方法,完成核心功能。
  4. 客户端使用 :客户端通过Image接口与代理交互,无需知道是否使用了代理,实现了对真实对象的透明访问。

四、常见代理类型与适用场景

代理模式有多种变体,根据功能不同可分为:

代理类型 核心功能 适用场景
远程代理 为远程对象(如网络服务)提供本地代理,隐藏网络通信细节。 分布式系统、RPC框架
虚拟代理 延迟创建开销大的对象(如示例中的延迟加载),优化性能。 大型图片加载、复杂对象初始化
保护代理 控制对真实对象的访问,验证权限或过滤请求。 权限管理、敏感操作保护
智能引用代理 在访问对象时添加额外操作(如计数引用、自动释放、日志记录)。 资源管理、调试日志
缓存代理 缓存真实对象的结果,重复请求直接返回缓存,减少计算或网络开销。 数据库查询缓存、API结果缓存

五、核心优势与与其他模式的区别

优势
  1. 功能增强:在不修改真实对象的前提下,为其添加额外功能(如日志、权限),符合开闭原则。
  2. 控制访问:可限制对真实对象的访问(如权限校验),保护敏感资源。
  3. 优化性能:通过延迟加载、缓存等机制,减少不必要的资源消耗。
  4. 隐藏细节:如远程代理隐藏网络通信细节,客户端无需关心对象位置。
与其他模式的区别
模式 核心差异点
代理模式 控制对对象的访问,添加额外功能,代理与真实对象实现同一接口,强调"访问控制"。
装饰器模式 动态为对象添加功能,不控制访问,装饰器通常嵌套使用,强调"功能扩展"。
适配器模式 转换接口使不兼容的类协作,不改变原功能,强调"接口适配"。
外观模式 为复杂子系统提供简化接口,不针对单个对象,强调"简化使用"。

六、实践建议

  1. 接口一致性:代理必须与真实对象实现相同接口,确保客户端透明使用。
  2. 单一职责:一个代理应专注于一种功能(如要么做权限控制,要么做缓存),避免设计复杂的"万能代理"。
  3. 延迟初始化:对开销大的真实对象,通过代理实现延迟创建,优化系统启动速度。
  4. 避免过度使用:简单场景下直接使用真实对象更高效,无需引入代理增加复杂性。

代理模式的核心价值在于"在不侵入原对象的前提下,灵活控制访问并增强功能"。它通过引入中间层,实现了客户端与真实对象的解耦,同时为系统扩展提供了便利。在需要权限控制、性能优化或功能增强的场景中,代理模式是一种优雅的解决方案。

相关推荐
1710orange2 小时前
java设计模式:适配器模式
java·设计模式·适配器模式
Cauhele浅能3 小时前
【嵌入式C快捷键设计】表驱动法实现
c语言·设计模式
庸人自扰614 小时前
常见设计模式讲解
设计模式
bkspiderx4 小时前
C++设计模式之行为型模式:解释器模式(Interpreter)
c++·设计模式·解释器模式
倔强菜鸟5 小时前
2025.8.10-学习C++(一)
开发语言·c++·学习
ZXF_H5 小时前
C/C++预定义宏与调试日志输出模板
开发语言·c++·日志·调试·预定义宏
2401_841495646 小时前
【数据结构】顺序表的基本操作
数据结构·c++·算法·顺序表·线性表·线性结构·顺序表的基本操作
小糖学代码6 小时前
STL的list模拟实现(带移动构造和emplace版本)
c语言·数据结构·c++·windows·list
王嘉俊9256 小时前
Qt 入门:构建跨平台 GUI 应用的强大框架
c语言·开发语言·c++·qt·入门·cpp