代理模式(C++示例)

📅 2026/7/19 15:43:56
代理模式(C++示例)
一、模式定义与核心思想代理模式是一种结构型设计模式它为另一个对象提供一个替身或占位符以控制对这个对象的访问。核心思想通过引入一个代理对象在不改变原始对象代码的前提下增加额外的功能如访问控制、延迟加载、日志审计等从而实现功能增强和解耦。二、主要解决问题代理模式主要解决的是在直接访问对象时可能遇到的问题例如创建成本搞、需要额外控件等。假设有一个Image接口和它的实现RealImage加载高清图片非常耗时且消耗内存。我们希望延迟加载只有真正显示图片时才加载。权限控制某些用户无权查看该图片。记录日志记录谁在什么时间访问了图片。如果将这些逻辑直接写在RealImage中会导致单一职责原则被破坏。RealImage应该只关心图片渲染而非权限或加载策略。代理模式完美解决了这个问题。三、模式结构代理模式主要包含三个主要角色1. Subject抽象主题声明了真实主题和代理主题的共同接口这样任何使用真实主题的地方都可以使用代理主题。#include iostream #include string // 抽象主题图片接口 class Image { public: virtual ~Image() default; // 基类析构函数必须是虚函数 virtual void display() 0; };2. RealSubject真实主题定义了代理所代表的真实对象是业务逻辑的具体执行者。// 真实主题实际加载并显示图片 class RealImage : public Image { private: std::string fileName; void loadFromDisk() { std::cout Loading heavy image from disk: fileName std::endl; } public: explicit RealImage(const std::string fileName) : fileName(fileName) { loadFromDisk(); // 构造函数中模拟昂贵操作 } void display() override { std::cout Displaying image: fileName std::endl; } };3. Proxy代理持有对真实主题的引用它实现了抽象主题的接口可以在调用真实主题的方法前后添加额外的操作预处理、后处理。// 代理控制对 RealImage 的访问 class ProxyImage : public Image { private: RealImage* realImage nullptr; // 持有真实对象的指针 std::string fileName; public: explicit ProxyImage(const std::string fileName) : fileName(fileName) {} ~ProxyImage() override { delete realImage; // 清理资源 } void display() override { // 虚拟代理的核心延迟初始化 (Lazy Initialization) if (realImage nullptr) { realImage new RealImage(fileName); } std::cout [Proxy] Checking permissions... std::endl; realImage-display(); std::cout [Proxy] Logging access. std::endl; } };四、模拟“动态代理”C 没有反射无法像 Java 那样在运行时随意生成代理类。但是我们可以通过std::function​ 和Lambda​ 来模拟一种灵活的“通用代理”这在某些场景下比继承更灵活类似于策略模式与代理模式的结合。适用场景性能监控统计函数耗时。统一的前置/后置处理如日志打印。不需要严格遵循 GoF 的类结构更注重函数行为的包装#include functional #include chrono #include iostream // 一个通用的代理执行器 class FunctionProxy { private: std::functionvoid() target; // 目标函数的包装 public: // 构造函数接收任何无参可调用对象 templatetypename Func explicit FunctionProxy(Func func) : target(std::forwardFunc(func)) {} // 执行代理逻辑 void execute() { std::cout [Proxy] Before execution. std::endl; auto start std::chrono::high_resolution_clock::now(); // 调用真实函数 if (target) { target(); } auto end std::chrono::high_resolution_clock::now(); std::chrono::durationdouble elapsed end - start; std::cout [Proxy] After execution. Time: elapsed.count() s std::endl; } }; // 业务类无需继承特定接口 class DataProcessor { public: void process() { std::cout Processing data... std::endl; // 模拟耗时操作 std::this_thread::sleep_for(std::chrono::milliseconds(500)); } }; int main() { DataProcessor processor; // 创建代理将成员函数绑定进去 FunctionProxy proxy([processor]() { processor.process(); }); proxy.execute(); return 0; }五、C 实现代理模式的注意事项虚函数开销基于多态的代理依赖于虚函数表查找vtable会有微小的运行时开销。在对性能极度敏感的底层代码中可能需要考虑 CRTP奇异递归模板模式来在编译期实现静态多态。接口稳定性代理类和真实类必须完全遵循同一个接口。如果接口频繁变动维护成本较高。拷贝语义如果你的代理类包含指针务必定义好拷贝构造函数、拷贝赋值运算符遵循 Rule of Three/Five或者使用std::unique_ptr禁止拷贝或者使用std::shared_ptr共享资源。