跳转到主内容
极星编程网:以代码为星,赴技术山海!

深入浅出代理模式:从生活场景到代码落地

深入浅出代理模式:从生活场景到代码落地 一、认识代理模式:生活中的“中间人”哲学 在日常生活里,“代理”的身影无处不在:网购时电商平台代理商家处理交易,出国旅游时旅行社代理当地服务商安排行程,明星的商业合作由经纪人代理对接……这些“中间人”本质上都在扮演“代理”角色——代替一方与另一方交互,并在过程中承担特定职责。

在设计模式体系中,代理模式(Proxy Pattern)正是对这种现实场景的抽象,其核心定义为: 为其他对象提供一种代理以控制对这个对象的访问 。

通过引入代理对象作为中间层,我们能在不修改原始对象代码的前提下,为其增加访问控制、日志记录、权限校验等额外逻辑,同时让真实对象专注于核心业务,完美契合“单一职责原则”。

比如房屋中介的场景:小明想买房但没时间筛选房源,委托中介帮忙——中介会先过滤掉面积不足100平方米的房子,只将符合条件的房源推荐给小明。

这里,小明是“真实需求方”,中介是“代理”,“买房”是核心行为,中介的存在既减轻了小明的负担,又实现了对房源的筛选控制,这就是代理模式的典型应用。

二、代理模式的核心结构与分类 1. 三大核心角色 代理模式的结构清晰,主要包含三个角色,它们之间的关系可通过以下UML类图体现:

对应“房屋中介”场景的具体UML类图如下,更贴合实际业务逻辑:

抽象主题(HouseBuyer) :通过接口或抽象类声明真实主题与代理的共同方法,是两者能互相替代的基础。

比如“买房”场景中,抽象主题就是“购房者”接口,定义“查看房源”“决定购买”等核心行为。

真实主题(RealBuyer) :代理所代表的真实对象,负责执行具体的核心业务逻辑。

比如场景中的“小明”,只关注“是否购买符合条件的房子”,不处理筛选房源等辅助工作。

代理(HouseAgentProxy) :持有真实主题的引用,实现与抽象主题一致的接口,在调用真实主题前后可添加额外逻辑。

比如场景中的“中介”,既会筛选房源(前置控制),又会记录看房日志(后置操作),还会将符合条件的需求传递给小明。

2. 常见代理类型与应用场景 根据功能差异,代理模式可分为多种类型,每种类型对应特定的解决场景: 虚拟代理 :用于延迟初始化重量级对象,避免资源浪费。

比如加载大型图片时,先显示缩略图(虚拟代理),待用户点击后再加载高清图(真实对象),减少初始加载时间。

安全代理 :控制对敏感对象的访问,校验权限。

比如系统后台接口,通过代理验证用户是否有管理员权限,无权限则拒绝访问真实接口。

远程代理 :为不同地址空间的对象提供局部代表,隐藏网络通信细节。

比如分布式系统中,客户端通过本地代理调用远程服务器的接口,无需关心IP、端口、协议等网络参数。

智能引用代理 :在访问对象时执行额外辅助操作。

比如记录对象的访问次数、当对象引用为0时自动释放资源、检测对象是否被正确锁定等。

三、代码实现:房屋中介代理案例 我们以“小明通过中介买房”为场景,用C++完整实现代理模式,清晰展示三大角色的协作逻辑:

#include

#include #include #include

using namespace std;

// 1. 抽象主题:购房者接口(定义共同行为) class HouseBuyer { public: virtual ~HouseBuyer() = default; // 核心方法:查看房源(参数为面积和房源信息) virtual void checkHouse(int area, const string& houseInfo) = 0; };

// 2. 真实主题:小明(实际购房者,专注核心业务) class RealBuyer : public HouseBuyer { private: string buyerName; // 真实购房者姓名 public: // 构造函数:初始化购房者姓名 RealBuyer(string name) : buyerName(name) {}

void checkHouse(int area, const string& houseInfo) override { // 真实主题只处理核心逻辑:确认房源符合需求后,决定查看 cout << "[" << buyerName << "] 收到符合条件的房源:" << endl; cout << "面积:" << area << "平方米 | 信息:" << houseInfo << endl; cout << "确认查看,准备后续沟通!

" << endl << endl; } };

// 3. 代理:房屋中介(处理辅助逻辑,控制访问) class HouseAgentProxy : public HouseBuyer { private: unique_ptr realBuyer; // 持有真实购房者的引用 int minArea; // 代理的筛选条件:最小房屋面积 vector viewLog; // 代理的额外数据:看房日志

public: // 构造函数:初始化真实购房者、筛选条件 HouseAgentProxy(string buyerName, int minimumArea) : realBuyer(make_unique(buyerName)), minArea(minimumArea) {}

void checkHouse(int area, const string& houseInfo) override { // 前置控制:筛选房源(代理的核心控制逻辑) if (area >= minArea) { cout << "[中介筛选] 房源面积" << area << "平方米,符合" << minArea << "平方米要求,推荐给购房者" << endl; // 调用真实主题的核心方法 realBuyer->checkHouse(area, houseInfo); } else { cout << "[中介筛选] 房源面积" << area << "平方米,未达" << minArea << "平方米要求,不推荐" << endl << endl; }

// 后置操作:记录看房日志(代理的额外功能) recordViewLog(area, houseInfo, area >= minArea); }

// 代理的额外方法:查看看房日志(客户端可通过代理获取日志,无需访问真实对象) void showViewLog() const { cout << "===== 看房日志汇总 =====" << endl; for (const auto& log : viewLog) { cout << log << endl; } cout << "======================" << endl; }

private: // 私有方法:记录日志(代理内部处理,对外隐藏实现) void recordViewLog(int area, const string& houseInfo, bool isPassed) { string log = "[" + to_string(area) + "㎡] " + houseInfo + " | 筛选结果:" + (isPassed ? "通过" : "未通过"); viewLog.push_back(log); } };

// 客户端代码:模拟用户使用代理处理需求 int main() { // 1. 创建代理对象(中介):指定购房者为"小明",最小面积100平方米 unique_ptr agent = make_unique("小明", 100);

// 2. 模拟4个待筛选的房源 vector> houses = { {95, "两室一厅,南北通透,中层"}, {120, "三室两厅,精装修,带阳台"}, {88, "两室两厅,一楼带院,老小区"}, {150, "四室两厅,江景房,新楼盘"} };

// 3. 客户端通过代理处理所有房源请求(不直接访问小明) for (const auto& house : houses) { agent->checkHouse(house.first, house.second); }

// 4. 客户端通过代理查看日志(额外功能,无需真实对象参与) agent->showViewLog();

return 0; }

代码运行结果

[中介筛选] 房源面积95平方米,未达100平方米要求,不推荐

[中介筛选] 房源面积120平方米,符合100平方米要求,推荐给购房者 [小明] 收到符合条件的房源: 面积:120平方米 | 信息:三室两厅,精装修,带阳台 确认查看,准备后续沟通!

[中介筛选] 房源面积88平方米,未达100平方米要求,不推荐

[中介筛选] 房源面积150平方米,符合100平方米要求,推荐给购房者 [小明] 收到符合条件的房源: 面积:150平方米 | 信息:四室两厅,江景房,新楼盘 确认查看,准备后续沟通!

===== 看房日志汇总 ===== [95㎡] 两室一厅,南北通透,中层 | 筛选结果:未通过 [120㎡] 三室两厅,精装修,带阳台 | 筛选结果:通过 [88㎡] 两室两厅,一楼带院,老小区 | 筛选结果:未通过 [150㎡] 四室两厅,江景房,新楼盘 | 筛选结果:通过 ======================

代码解析 抽象主题(HouseBuyer) :定义

checkHouse

纯虚函数,统一“购房者”和“中介”的行为,确保中介能完全替代小明处理房源请求。

真实主题(RealBuyer) :仅实现核心逻辑——收到符合条件的房源后确认查看,不涉及筛选、日志等辅助操作,符合“单一职责原则”。

代理(HouseAgentProxy) :承担了三项关键工作:一是筛选房源(前置控制,只推荐面积达标的房源);二是记录日志(后置操作,保存所有房源的筛选结果);三是对外提供日志查询接口(扩展功能,客户端无需访问小明就能获取看房记录)。

客户端 :全程只与代理(中介)交互,既不知道真实主题(小明)的存在,也无需关心筛选、日志的实现细节,符合“迪米特法则”(最少知识原则)。

四、代理模式的优缺点与模式辨析 1. 优缺点分析 优点 职责分离 :真实对象专注核心业务,代理处理辅助逻辑,代码结构更清晰,便于维护。

比如小明只关心买房,中介只关心筛选和记录,两者修改互不影响。

保护真实对象 :通过代理控制访问,避免真实对象直接暴露给客户端,保护敏感信息。

比如系统核心接口通过代理校验权限,防止未授权访问。

灵活扩展 :无需修改真实对象和客户端,只需修改代理就能增加新功能。

比如要给“买房”场景增加“价格筛选”,只需在中介的

checkHouse

方法中添加价格判断逻辑。

支持远程访问 :通过远程代理可透明访问异地对象,隐藏网络细节。

比如客户端调用云服务器接口,只需通过本地代理,无需处理HTTP请求、数据序列化等操作。

缺点 增加系统复杂性 :引入代理会增加类的数量和交互层次,比如“买房”场景原本只有“小明”一个类,现在多了“中介”和“购房者接口”,代码结构更复杂。

可能影响性能 :代理的额外处理(如筛选、日志)会带来轻微性能开销,尤其是高频访问场景。

比如每秒 thousands 级的接口调用,代理的日志记录可能成为性能瓶颈。

调试难度增加 :请求需经过代理转发,问题排查时需多一层跟踪。

比如客户端反馈“无法查看房源”,需同时检查中介的筛选逻辑和小明的核心逻辑,定位问题更耗时。

2. 与装饰模式的辨析 代理模式与装饰模式的核心差异 初学者常混淆代理模式与装饰模式,二者虽都通过“包装对象”扩展功能,但本质差异显著: 核心意图不同 :代理模式聚焦“访问控制”,如权限过滤、远程调用或延迟加载(例:中介筛选房源,控制购房者可见范围);装饰模式侧重“功能增强”,动态为对象添加行为(例:装修公司为房屋加装智能门锁)。

对象关系与组合逻辑 :代理模式中代理与真实对象多为一对一固定关联,类型在编译期确定(如静态代理类);装饰模式支持装饰器多层嵌套(多对一),运行时动态组合(如文件流依次叠加缓冲、加密装饰器)。

客户端感知差异 :代理模式下客户端可能不知真实对象存在(如远程服务代理);装饰模式中客户端明确知晓功能叠加过程(如图形组件添加滚动条装饰)。

典型场景 :代理模式适用于权限控制、延迟加载等;装饰模式适用于日志记录、缓存处理等需灵活扩展功能的场景。

二者通过“控制访问”与“功能扩展”的核心意图,形成清晰的应用边界。

五、总结 代理模式是一种“控制型”设计模式,通过引入中间层(代理对象),平衡了“核心业务”与“辅助逻辑”的关系,既保证真实对象的纯粹性,又灵活扩展了系统功能。

其核心价值在于“隔离”——隔离客户端与真实对象,隔离核心业务与辅助操作,让系统更符合面向对象设计的核心原则。

在C++智能指针实现上,

std::shared_ptr

就是代理模式的体现,它作为代理对象,管理动态分配的内存(真实对象),自动处理内存释放,避免内存泄漏,将资源管理的辅助操作与核心业务逻辑分离。

在网络通信场景中,可创建网络请求代理类,代理真实的网络请求对象,在代理类中添加请求缓存、超时重试等辅助逻辑,提升网络请求的稳定性和效率。

此外,在图形渲染中,对于复杂图形对象,可使用虚拟代理,在需要显示图形时,再加载真实图形数据进行渲染,实现延迟加载,优化程序的初始化性能。

本文相关概念、案例解析及模式辨析参考自《大话设计模式》。

相关文章