代理模式C++

代理模式(Proxy Pattern)是一种结构型设计模式,它为另一个对象提供一个代理或占位符,以控制对原对象的访问。这种模式通过引入代理对象,在不改变原对象的前提下,实现对原对象的访问控制、增强或扩展。

代理模式的核心角色

  1. Subject(抽象主题):定义原对象和代理对象的共同接口,使代理可被当作原对象使用
  2. RealSubject(真实主题):被代理的实际对象,提供核心业务逻辑
  3. Proxy(代理):实现抽象主题,持有真实主题的引用,控制对真实主题的访问

常见代理类型及C++实现示例

1. 远程代理(Remote Proxy)

为远程对象提供本地代理,隐藏网络通信细节。

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

// 抽象主题:文件接口
class File {
public:
    virtual ~File() = default;
    virtual void read() = 0;
    virtual void write(const std::string& content) = 0;
};

// 真实主题:远程文件(位于服务器)
class RemoteFile : public File {
private:
    std::string fileName;
    std::string serverAddress;

public:
    RemoteFile(std::string name, std::string address)
        : fileName(std::move(name)), serverAddress(std::move(address)) {
        // 模拟远程连接
        std::cout << "连接到远程服务器: " << serverAddress << std::endl;
    }

    void read() override {
        // 模拟远程读取
        std::cout << "从 " << serverAddress << " 读取文件: " << fileName << std::endl;
    }

    void write(const std::string& content) override {
        // 模拟远程写入
        std::cout << "向 " << serverAddress << " 的 " << fileName 
                  << " 写入内容: " << content << std::endl;
    }
};

// 代理:远程文件代理(本地代理)
class RemoteFileProxy : public File {
private:
    std::string fileName;
    std::string serverAddress;
    RemoteFile* remoteFile = nullptr;  // 延迟初始化远程对象

    // 确保远程对象已初始化
    void ensureConnected() {
        if (!remoteFile) {
            // 可以在这里添加权限校验、连接池等逻辑
            try {
                remoteFile = new RemoteFile(fileName, serverAddress);
            } catch (const std::exception& e) {
                std::cerr << "连接远程文件失败: " << e.what() << std::endl;
                throw;
            }
        }
    }

public:
    RemoteFileProxy(std::string name, std::string address)
        : fileName(std::move(name)), serverAddress(std::move(address)) {}

    ~RemoteFileProxy() override {
        delete remoteFile;  // 释放远程对象
    }

    // 代理读取操作
    void read() override {
        std::cout << "RemoteFileProxy: 准备读取远程文件" << std::endl;
        ensureConnected();
        remoteFile->read();
        std::cout << "RemoteFileProxy: 读取完成" << std::endl;
    }

    // 代理写入操作
    void write(const std::string& content) override {
        std::cout << "RemoteFileProxy: 准备写入远程文件" << std::endl;
        ensureConnected();
        remoteFile->write(content);
        std::cout << "RemoteFileProxy: 写入完成" << std::endl;
    }
};

// 客户端代码
void clientCode(File* file) {
    file->read();
    file->write("代理模式测试内容");
}

int main() {
    try {
        // 使用代理访问远程文件,客户端无需知道远程细节
        File* fileProxy = new RemoteFileProxy("data.txt", "http://remote-server.com");
        clientCode(fileProxy);
        delete fileProxy;
    } catch (const std::exception& e) {
        std::cerr << "客户端错误: " << e.what() << std::endl;
    }
    return 0;
}
2. 虚拟代理(Virtual Proxy)

延迟创建开销大的对象,仅在需要时才初始化。

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

// 抽象主题:图片接口
class Image {
public:
    virtual ~Image() = default;
    virtual void display() = 0;
};

// 真实主题:大图片(加载开销大)
class BigImage : public Image {
private:
    std::string filePath;

    // 模拟加载大图片的耗时操作
    void loadFromDisk() {
        std::cout << "正在从磁盘加载大图片: " << filePath << " (耗时操作)" << std::endl;
    }

public:
    BigImage(std::string path) : filePath(std::move(path)) {
        loadFromDisk();  // 构造时加载
    }

    void display() override {
        std::cout << "显示图片: " << filePath << std::endl;
    }
};

// 虚拟代理:图片代理(延迟加载)
class ImageProxy : public Image {
private:
    std::string filePath;
    std::unique_ptr<BigImage> bigImage;  // 延迟初始化

public:
    ImageProxy(std::string path) : filePath(std::move(path)) {
        std::cout << "创建图片代理: " << filePath << " (未实际加载)" << std::endl;
    }

    // 仅在需要显示时才加载真实图片
    void display() override {
        std::cout << "ImageProxy: 准备显示图片" << std::endl;
        if (!bigImage) {
            bigImage = std::make_unique<BigImage>(filePath);  // 延迟加载
        }
        bigImage->display();
    }
};

// 客户端代码
void showImage(Image* image) {
    std::cout << "请求显示图片..." << std::endl;
    image->display();
    std::cout << "图片显示结束" << std::endl;
}

int main() {
    // 创建代理时不会加载图片
    Image* image = new ImageProxy("large_image.jpg");
    
    // 执行其他操作,图片未被加载
    std::cout << "\n执行一些其他操作..." << std::endl;
    
    // 只有当需要显示时,才会真正加载图片
    std::cout << "\n第一次显示图片:" << std::endl;
    showImage(image);
    
    // 第二次显示时,已加载过,直接显示
    std::cout << "\n第二次显示图片:" << std::endl;
    showImage(image);
    
    delete image;
    return 0;
}
3. 保护代理(Protection Proxy)

控制对敏感对象的访问,基于权限过滤请求。

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

// 用户类:存储用户信息和权限
class User {
private:
    std::string name;
    bool isAdmin;

public:
    User(std::string n, bool admin) : name(std::move(n)), isAdmin(admin) {}
    
    const std::string& getName() const { return name; }
    bool hasAdminRights() const { return isAdmin; }
};

// 抽象主题:敏感数据接口
class SensitiveData {
public:
    virtual ~SensitiveData() = default;
    virtual std::string read() const = 0;
    virtual void write(const std::string& data) = 0;
};

// 真实主题:实际的敏感数据
class RealSensitiveData : public SensitiveData {
private:
    std::string data;

public:
    RealSensitiveData(std::string d) : data(std::move(d)) {}

    std::string read() const override {
        return data;
    }

    void write(const std::string& newData) override {
        data = newData;
    }
};

// 保护代理:控制敏感数据的访问
class ProtectionProxy : public SensitiveData {
private:
    RealSensitiveData* realData;
    User user;  // 当前用户

public:
    ProtectionProxy(RealSensitiveData* data, User u)
        : realData(data), user(std::move(u)) {}

    ~ProtectionProxy() override {
        delete realData;
    }

    // 读取权限控制:普通用户可读,管理员可写
    std::string read() const override {
        std::cout << "ProtectionProxy: " << user.getName() << " 请求读取数据" << std::endl;
        // 任何人都可以读取
        return realData->read();
    }

    // 写入权限控制:仅管理员可写
    void write(const std::string& newData) override {
        std::cout << "ProtectionProxy: " << user.getName() << " 请求写入数据" << std::endl;
        if (user.hasAdminRights()) {
            realData->write(newData);
            std::cout << "ProtectionProxy: 写入成功(管理员权限)" << std::endl;
        } else {
            throw std::runtime_error("权限不足:只有管理员可以修改敏感数据");
        }
    }
};

// 客户端代码
void accessData(SensitiveData* data) {
    try {
        std::cout << "读取数据: " << data->read() << std::endl;
        data->write("更新的数据内容");
        std::cout << "更新后的数据: " << data->read() << std::endl;
    } catch (const std::exception& e) {
        std::cerr << "操作失败: " << e.what() << std::endl;
    }
}

int main() {
    // 创建真实敏感数据
    RealSensitiveData* realData = new RealSensitiveData("初始敏感数据");
    
    std::cout << "=== 管理员访问 ===" << std::endl;
    User admin("管理员", true);
    SensitiveData* adminProxy = new ProtectionProxy(realData, admin);
    accessData(adminProxy);
    
    std::cout << "\n=== 普通用户访问 ===" << std::endl;
    // 注意:这里需要创建新的真实数据,因为上一个代理已经接管了realData的所有权
    RealSensitiveData* realData2 = new RealSensitiveData("初始敏感数据");
    User guest("普通用户", false);
    SensitiveData* guestProxy = new ProtectionProxy(realData2, guest);
    accessData(guestProxy);
    
    delete adminProxy;
    delete guestProxy;
    return 0;
}

代理模式的核心特点

  • 控制访问:代理可在调用真实对象前/后添加逻辑(如权限校验、日志记录)
  • 延迟初始化:虚拟代理可推迟昂贵对象的创建,直到真正需要时
  • 隐藏细节:远程代理屏蔽了对象位于远程的事实,使客户端透明访问
  • 增强功能:在不修改原对象的情况下,通过代理添加额外功能(如缓存、计数)

代理模式与装饰器模式的区别

特性 代理模式 装饰器模式
核心目的 控制对对象的访问 为对象动态添加功能
关系 代理通常知道真实对象的类型 装饰器可嵌套,通常不知道具体装饰的对象
使用场景 权限控制、远程访问、延迟加载 功能扩展、组合多个功能
透明度 客户端可能知道代理的存在 客户端通常不知道装饰器的存在

适用场景

  • 当需要控制对敏感对象的访问时(保护代理)
  • 当对象创建成本高,希望延迟初始化时(虚拟代理)
  • 当需要访问远程对象,隐藏网络细节时(远程代理)
  • 当需要为对象的访问添加日志、缓存等功能时(智能引用代理)

代理模式在框架中应用广泛,如RPC框架中的服务代理、ORM框架中的数据库代理、AOP中的方法拦截等。

相关推荐
small_wh1te_coder22 分钟前
GCC深度剖析:从编译原理到嵌入式底层实战
汇编·c++·面试·嵌入式·状态模式·c
汤永红1 小时前
week1-[循环嵌套]蛇
数据结构·c++·算法
极客BIM工作室1 小时前
谈谈《More Effective C++》的条款30:代理类
java·开发语言·c++
用户84913717547162 小时前
JustAuth实战系列(第11期):测试驱动开发 - 质量保证与重构实践
java·设计模式·单元测试
华科云商xiao徐2 小时前
突破Python性能墙:关键模块C++化的爬虫优化指南
c++·爬虫·python
Jooolin2 小时前
【教你一招】反汇编有啥用?
c++·ai编程·汇编语言
xnglan4 小时前
蓝桥杯手算题和杂题简易做法
数据结构·数据库·c++·python·算法·职场和发展·蓝桥杯
汤永红4 小时前
week1-[顺序结构]大海
c++·算法·信睡奥赛
我们从未走散5 小时前
设计模式学习笔记-----单例模式
java·笔记·学习·单例模式·设计模式