代理模式(Proxy Pattern)详解
·
代理模式(Proxy Pattern)详解
一、什么是代理模式?
代理模式是一种结构型设计模式,通过创建代理对象来间接访问目标对象(委托类),实现对目标对象的访问控制、权限验证、延迟加载或功能增强。代理对象起到中介作用,它不仅仅是对目标对象的直接替代,还能在访问目标对象时增加一些额外的逻辑。
二、代理模式的应用场景
- 权限控制:只有特定角色才能访问某些资源或功能。
- 远程访问:通过代理访问分布式系统中的远程对象(如RMI)。
- 延迟加载(虚拟代理):只有在需要时才创建并初始化资源密集型对象。
- 记录日志:在方法调用时记录日志信息。
- 缓存代理:对目标对象的返回值进行缓存,减少对目标对象的直接访问。
- 智能引用:当访问对象时,执行额外操作,如引用计数或对象销毁检查。
三、现实中的例子
- 公司助理(代理人):
- 助理根据客户身份控制是否允许客户见到老板。
- 助理可以代替老板处理简单事务,减少老板的工作量。
- 视频网站权限控制:
- 免费用户只能观看免费内容。
- VIP用户可以观看更多高级内容。
- 某些高级内容需VIP加额外券才能观看。
四、代理模式的组成
-
抽象类或接口:
定义代理类和目标类的公共方法。代理类和目标类都实现该接口,保证代理和目标对象可以互换。class VideoSet { public: virtual void freeMovie() = 0; virtual void vipMovie() = 0; virtual void ticketMovie() = 0; virtual ~VideoSet() = default; }; -
目标类(委托类):
实现了具体业务逻辑,是实际处理请求的类。class FixBugVideoSet : public VideoSet { public: void freeMovie() override { std::cout << "观看免费电影" << std::endl; } void vipMovie() override { std::cout << "观看VIP电影" << std::endl; } void ticketMovie() override { std::cout << "用券观看电影" << std::endl; } }; -
代理类:
代理类通过持有目标类的引用或指针间接访问目标类。代理类可以在调用目标类方法前后添加额外逻辑。class ProxyVideoSet : public VideoSet { private: VideoSet* realVideoSet; // 持有目标对象的指针 public: ProxyVideoSet(VideoSet* videoSet) : realVideoSet(videoSet) {} void freeMovie() override { realVideoSet->freeMovie(); // 直接调用目标对象方法 } void vipMovie() override { std::cout << "您目前是普通游客,需要升级为VIP才能观看VIP电影" << std::endl; } void ticketMovie() override { std::cout << "您没有电影券,需要购买券才能观看此电影" << std::endl; } }; -
客户端:
客户端通过代理类访问目标对象。客户端只知道代理类的存在,不需要直接操作目标对象。int main() { FixBugVideoSet realVideoSet; ProxyVideoSet proxy(&realVideoSet); proxy.freeMovie(); // 免费电影正常观看 proxy.vipMovie(); // 提示需要VIP权限 proxy.ticketMovie(); // 提示需要购买券 return 0; }
五、代理模式的分类
-
静态代理:
- 代理类在编译时由开发者定义,逻辑固定。
- 实现简单,但扩展性较差。
-
动态代理:
- 在运行时动态生成代理对象。
- 动态代理通过拦截方法调用并添加逻辑,适合场景复杂的情况(如Java的
InvocationHandler)。 - C++中可使用装饰器模式与动态代理结合实现类似效果。
-
保护代理:
- 控制对象的访问权限。
- 例如视频网站的用户分级功能,普通用户只能访问免费内容。
-
虚拟代理:
- 延迟加载目标对象。
- 例如在图片加载场景中,只有图片被实际访问时才初始化。
-
远程代理:
- 本地代理代替客户端访问远程对象。
- 常见于分布式系统中的RMI或gRPC。
-
缓存代理:
- 对目标对象的返回值进行缓存。
- 例如数据库查询代理,减少重复查询。
六、代理模式的优缺点
-
优点:
- 访问控制:实现灵活的权限验证。
- 职责分离:目标对象专注于核心功能,代理对象负责额外操作。
- 增强功能:可在不修改目标类的情况下添加新功能,如日志、缓存等。
- 提高可扩展性:通过引入代理,方便后续修改和扩展。
-
缺点:
- 系统复杂性增加:引入代理类可能使代码结构更加复杂。
- 性能开销:代理对象增加了一层间接调用,可能影响系统性能。
七、代理模式与其他设计模式的区别
- 装饰器模式:
- 代理模式侧重于控制访问,而装饰器模式是为了增强功能。
- 装饰器模式通常在不改变对象接口的情况下,动态为对象添加行为。
- 适配器模式:
- 适配器模式用于改变接口以兼容旧代码,代理模式则是为了控制访问和权限。
八、代理模式的完整案例
#include <iostream>
#include <memory>
class VideoSet {
public:
virtual void freeMovie() = 0;
virtual void vipMovie() = 0;
virtual void ticketMovie() = 0;
virtual ~VideoSet() = default;
};
class FixBugVideoSet : public VideoSet {
public:
void freeMovie() override {
std::cout << "观看免费电影" << std::endl;
}
void vipMovie() override {
std::cout << "观看VIP电影" << std::endl;
}
void ticketMovie() override {
std::cout << "用券观看电影" << std::endl;
}
};
class ProxyVideoSet : public VideoSet {
private:
std::unique_ptr<VideoSet> realVideoSet;
bool isVIP;
bool hasTicket;
public:
ProxyVideoSet(bool vip, bool ticket)
: realVideoSet(std::make_unique<FixBugVideoSet>()), isVIP(vip), hasTicket(ticket) {}
void freeMovie() override {
realVideoSet->freeMovie();
}
void vipMovie() override {
if (isVIP) {
realVideoSet->vipMovie();
} else {
std::cout << "您目前是普通游客,需要升级为VIP才能观看VIP电影" << std::endl;
}
}
void ticketMovie() override {
if (isVIP && hasTicket) {
realVideoSet->ticketMovie();
} else if (!isVIP) {
std::cout << "您需要升级为VIP才能购买电影券" << std::endl;
} else {
std::cout << "您没有电影券,请先购买" << std::endl;
}
}
};
int main() {
ProxyVideoSet visitor(false, false); // 普通用户
ProxyVideoSet vipUser(true, true); // VIP用户
visitor.freeMovie(); // 观看免费电影
visitor.vipMovie(); // 提示需要升级为VIP
visitor.ticketMovie();// 提示需要购买电影券
vipUser.freeMovie(); // VIP用户观看免费电影
vipUser.vipMovie(); // VIP用户观看VIP电影
vipUser.ticketMovie();// VIP用户观看用券电影
return 0;
}
九、小结
代理模式通过引入代理对象,灵活地控制目标对象的访问权限,同时为目标对象添加额外功能而无需修改其代码。其核心在于间接性和扩展性,适用于权限管理、延迟加载和功能增强等场景。
更多推荐




所有评论(0)