代理模式(Proxy Pattern)是一种结构型设计模式,它为其他对象提供一种代理(Placeholder)以控制对这个对象的访问。代理模式通过引入一个代理对象,在不改变原始对象接口的前提下,为其增加额外的功能,如访问控制、延迟加载、日志记录等。

代理模式的核心角色

  1. 抽象主题(Subject):定义代理和真实主题的共同接口
  2. 真实主题(Real Subject):实际执行业务逻辑的对象
  3. 代理(Proxy):实现抽象主题接口,包含对真实主题的引用,控制对真实主题的访问

代理模式的常见类型及实现

1. 虚拟代理(Virtual Proxy)

延迟创建开销较大的对象,直到真正需要时才初始化。

#include <iostream>
#include <string>

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

// 真实主题:高分辨率图片
class HighResolutionImage : public Image {
private:
    std::string filename;

    // 模拟加载图片的耗时操作
    void loadFromDisk() {
        std::cout << "从磁盘加载高分辨率图片: " << filename << std::endl;
    }

public:
    HighResolutionImage(const std::string& name) : filename(name) {
        // 实际项目中,这里可能包含耗时的加载操作
    }

    void display() override {
        loadFromDisk();
        std::cout << "显示高分辨率图片: " << filename << std::endl;
    }
};

// 虚拟代理:延迟加载图片
class ImageProxy : public Image {
private:
    HighResolutionImage* realImage;
    std::string filename;

    // 懒加载:只在需要时创建真实对象
    void ensureImageLoaded() {
        if (!realImage) {
            realImage = new HighResolutionImage(filename);
        }
    }

public:
    ImageProxy(const std::string& name) : filename(name), realImage(nullptr) {}

    ~ImageProxy() override {
        delete realImage;
    }

    void display() override {
        ensureImageLoaded();
        realImage->display();
    }
};

// 客户端使用
int main() {
    std::cout << "创建图片代理..." << std::endl;
    Image* image = new ImageProxy("风景照.jpg");  // 此时并未加载真实图片

    std::cout << "\n第一次显示图片:" << std::endl;
    image->display();  // 第一次显示时才真正加载

    std::cout << "\n第二次显示图片:" << std::endl;
    image->display();  // 直接使用已加载的图片

    delete image;
    return 0;
}
2. 保护代理(Protection Proxy)

控制对敏感对象的访问,根据权限决定是否允许操作。

#include <iostream>
#include <string>

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

public:
    User(const std::string& userName, bool admin) 
        : name(userName), isAdmin(admin) {}

    bool isAdministrator() const {
        return isAdmin;
    }

    std::string getName() const {
        return name;
    }
};

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

// 真实主题:实际文件
class RealFile : public File {
private:
    std::string filename;

public:
    RealFile(const std::string& name) : filename(name) {}

    void read() override {
        std::cout << "读取文件内容: " << filename << std::endl;
    }

    void write(const std::string& content) override {
        std::cout << "向文件 " << filename << " 写入内容: " << content << std::endl;
    }
};

// 保护代理:控制文件访问权限
class FileProxy : public File {
private:
    RealFile* realFile;
    User* user;

public:
    FileProxy(const std::string& filename, User* user)
        : realFile(new RealFile(filename)), user(user) {}

    ~FileProxy() override {
        delete realFile;
    }

    void read() override {
        // 任何人都可以读取
        std::cout << "用户 " << user->getName() << " 尝试读取文件..." << std::endl;
        realFile->read();
    }

    void write(const std::string& content) override {
        // 只有管理员可以写入
        if (user->isAdministrator()) {
            std::cout << "管理员 " << user->getName() << " 尝试写入文件..." << std::endl;
            realFile->write(content);
        } else {
            std::cout << "权限不足! 用户 " << user->getName() << " 没有写入权限。" << std::endl;
        }
    }
};

// 客户端使用
int main() {
    // 创建普通用户和管理员
    User* normalUser = new User("张三", false);
    User* adminUser = new User("李四", true);

    // 使用代理访问文件
    File* file = new FileProxy("重要文档.txt", normalUser);
    std::cout << "=== 普通用户操作 ===" << std::endl;
    file->read();
    file->write("添加一些内容");

    // 更换用户
    delete file;
    file = new FileProxy("重要文档.txt", adminUser);
    std::cout << "\n=== 管理员操作 ===" << std::endl;
    file->read();
    file->write("添加一些内容");

    // 清理资源
    delete file;
    delete normalUser;
    delete adminUser;

    return 0;
}

代理模式的其他常见类型

  • 远程代理(Remote Proxy):为远程对象提供本地代表,常用于分布式系统
  • 智能引用(Smart Reference):在访问对象时执行额外操作,如计数引用、自动释放等
  • 缓存代理(Cache Proxy):为 expensive 操作的结果提供缓存,如数据库查询缓存

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

  • 代理模式:主要控制对对象的访问,侧重于"控制"
  • 装饰器模式:主要为对象添加功能,侧重于"增强"

代理模式的应用场景

  1. 延迟加载大对象(虚拟代理)
  2. 控制对敏感资源的访问(保护代理)
  3. 远程服务本地代理(远程代理)
  4. 实现日志记录、性能监控等横切关注点
  5. 实现对象池、缓存等功能

代理模式的优缺点

优点

  • 可以在不修改原始对象的情况下添加额外功能
  • 实现了客户端与真实主题的解耦
  • 可以灵活控制对真实对象的访问
  • 符合开放-封闭原则,易于扩展

缺点

  • 增加了系统复杂度,需要理解代理与真实对象的关系
  • 可能导致请求处理速度变慢(多了一层代理)
  • 对于复杂代理,实现起来可能比较繁琐

代理模式在C++中有着广泛应用,例如智能指针(std::shared_ptrstd::unique_ptr)就是一种智能引用代理,负责自动管理内存;某些ORM框架中的数据库访问代理也是典型应用。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐