代理模式(Proxy Pattern)

C++ 代理模式(Proxy Pattern)


目录

  1. 模式定义与核心思想
  2. 解决的核心业务痛点
  3. 标准三大角色 & UML结构说明
  4. 代理模式核心分类(5种)
  5. C++ 标准静态代理完整实现
  6. 五种代理类型 C++ 实战代码
  7. 静态代理 与 动态代理 详解
  8. 代理 VS 装饰器 VS 适配器 深度辨析
  9. 适用场景 & 禁用场景
  10. 优缺点精细拆解
  11. C++ 工程开发最佳实践规范
  12. 常见坑点与完整避坑指南
  13. 面试高频深挖题(含标准答案)
  14. C++ 开源框架 & 实际项目落地

1. 模式定义与核心思想

定义

代理模式是结构型设计模式 ,给真实业务对象 提供一个代理占位对象 ,由代理对象控制对真实对象的访问,客户端始终访问代理,间接调用真实对象。

核心特征

  • 代理与真实对象实现同一个抽象接口
  • 客户端无感知,完全透明
  • 代理可以前置拦截、后置处理、延迟创建、权限校验、缓存结果
  • 不修改真实对象任何代码,符合开闭原则

生活化类比

  • 明星经纪人:粉丝找经纪人 → 经纪人对接明星
  • 房产中介:客户找中介 → 中介对接房东
  • 网络代理:本机请求 → 代理服务器 → 目标网站

核心一句话

不直接访问真身,通过中间人统一管控访问逻辑。


2. 解决的核心业务痛点

  1. 真实对象创建成本极高
    加载大图、初始化连接、读取大配置、数据库复杂查询,没必要提前初始化。
  2. 需要做访问权限控制
    不同角色只能访问指定接口,统一在代理层拦截校验。
  3. 重复请求浪费资源
    相同查询、相同计算结果可缓存,代理层统一缓存。
  4. 需要统一横切逻辑
    日志、耗时、限流、鉴权、监控,全部放在代理层,不侵入业务。
  5. 隐藏真实对象实现细节
    对外只暴露代理,屏蔽底层复杂逻辑与远程地址。
  6. 远程对象调用
    RPC、微服务跨进程调用,本地代理占位,转发网络请求。

3. 标准三大角色 & UML结构

角色名称 英文 职责详解
抽象主题 Subject 统一抽象接口,代理类、真实类必须共同实现
真实主题 RealSubject 真正实现业务逻辑的核心对象
代理类 Proxy 持有真实对象引用/指针,实现相同接口,控制访问、附加逻辑

调用流程

客户端 → 抽象接口 → 代理Proxy → 前置处理 → 调用真实对象 → 后置处理 → 返回客户端


4. 代理模式核心分类(5种常用)

  1. 静态代理:编译期写死代理类,一对一代理
  2. 虚拟代理:懒加载,用到时才创建真实对象
  3. 保护代理:权限拦截,控制不同角色访问
  4. 缓存代理:缓存重复计算/查询结果,提升性能
  5. 远程代理:本地占位,代理远程跨进程/网络对象

5. C++ 标准静态代理完整实现

5.1 抽象主题接口

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

// 抽象主题
class Subject
{
public:
    virtual ~Subject() = default;
    virtual void request(const std::string& info) = 0;
};

5.2 真实主题

cpp 复制代码
// 真实主题
class RealSubject : public Subject
{
public:
    void request(const std::string& info) override
    {
        std::cout << "真实对象执行业务:" << info << "\n";
    }
};

5.3 静态代理类

cpp 复制代码
// 静态代理
class StaticProxy : public Subject
{
private:
    std::unique_ptr<RealSubject> realObj;
public:
    void request(const std::string& info) override
    {
        // 前置增强
        std::cout << "代理:前置日志记录\n";

        // 懒创建真实对象
        if (!realObj)
        {
            realObj = std::make_unique<RealSubject>();
        }

        // 调用真实业务
        realObj->request(info);

        // 后置增强
        std::cout << "代理:后置统计收尾\n";
    }
};

5.4 客户端调用

cpp 复制代码
int main()
{
    std::unique_ptr<Subject> sub = std::make_unique<StaticProxy>();
    sub->request("用户业务请求");
    return 0;
}

6. 五种代理类型 C++ 实战代码

6.1 虚拟代理(懒加载)

适用:大图、大资源、重初始化对象。

cpp 复制代码
class BigResource
{
public:
    void load()
    {
        std::cout << "加载超大资源,耗时很久...\n";
    }
    void show()
    {
        std::cout << "展示资源内容\n";
    }
};

class VirtualProxy
{
private:
    mutable std::unique_ptr<BigResource> res;
public:
    void show()
    {
        if (!res)
        {
            res = std::make_unique<BigResource>();
            res->load();
        }
        res->show();
    }
};

6.2 保护代理(权限控制)

cpp 复制代码
enum class UserRole
{
    Admin,
    Normal
};

class SecretData
{
public:
    void view()
    {
        std::cout << "查看敏感机密数据\n";
    }
};

class ProtectProxy
{
private:
    UserRole role;
    std::unique_ptr<SecretData> data;
public:
    explicit ProtectProxy(UserRole r) : role(r), data(std::make_unique<SecretData>()) {}

    void view()
    {
        if (role != UserRole::Admin)
        {
            std::cout << "权限不足,禁止访问\n";
            return;
        }
        data->view();
    }
};

6.3 缓存代理(结果缓存)

cpp 复制代码
class QueryService
{
public:
    std::string query(const std::string& key)
    {
        // 模拟耗时查询
        std::cout << "数据库真实查询:" << key << "\n";
        return key + "_result";
    }
};

class CacheProxy
{
private:
    QueryService service;
    std::unordered_map<std::string, std::string> cache;
public:
    std::string query(const std::string& key)
    {
        if (cache.find(key) != cache.end())
        {
            std::cout << "代理:命中缓存,直接返回\n";
            return cache[key];
        }
        std::string res = service.query(key);
        cache[key] = res;
        return res;
    }
};

6.4 远程代理(模拟)

本地代理占位,屏蔽网络请求细节,客户端只调本地接口。

cpp 复制代码
class RemoteService
{
public:
    void remoteCall()
    {
        std::cout << "远程服务器执行业务\n";
    }
};

class RemoteProxy
{
public:
    void remoteCall()
    {
        std::cout << "代理:建立网络连接、封装协议\n";
        RemoteService().remoteCall();
        std::cout << "代理:关闭连接、解析返回\n";
    }
};

6.5 智能引用代理

C++ std::shared_ptr/unique_ptr 就是典型智能代理:

  • 生命周期管理
  • 引用计数
  • 自动析构释放

7. 静态代理 与 动态代理

静态代理

  • 编译期手动编写代理类
  • 一个代理只能代理一个真实类
  • 类膨胀严重,新增业务需新增代理
  • C++ 无原生动态代理,只能手动模拟

动态代理

  • 运行时动态生成代理类
  • 一个代理可代理多个目标类
  • Java 有 JDK 动态代理 / Cglib
  • C++ 可通过 模板、泛型、宏 简易实现通用代理

8. 代理 VS 装饰器 VS 适配器 深度辨析

模式 核心意图 接口是否改变 侧重点
代理模式 控制访问、隔离拦截 接口完全一致 权限、懒加载、缓存、隐藏真身
装饰器模式 动态叠加功能增强 接口完全一致 日志、加配料、多层扩展
适配器模式 接口转换兼容 接口发生改变 适配旧接口、第三方不兼容接口

一句话精准区分

  • 管控访问、隔离真身 → 代理
  • 层层加功能、动态扩展 → 装饰器
  • 接口不匹配、做转换适配 → 适配器

关键细节区别

  • 代理倾向自己创建真实对象,重在控制;
  • 装饰器由外部传入被装饰对象,重在组合增强。

9. 适用场景 & 禁用场景

适用场景

  1. 对象创建/初始化成本高,需要懒加载
  2. 需要统一权限校验、访问控制
  3. 频繁重复查询/计算,需要缓存代理
  4. RPC、微服务、跨进程远程调用
  5. 需要统一日志、耗时、限流、监控横切逻辑
  6. 隐藏真实业务实现,对外屏蔽细节
  7. 控制对象生命周期、引用计数管理

禁用场景

  1. 业务逻辑极其简单,无任何管控、增强需求
  2. 对象创建开销极小,没必要懒加载
  3. 接口需要大幅改造,应使用适配器而非代理

10. 优缺点精细拆解

优点

  1. 职责分离:真实对象只关注核心业务,代理处理管控逻辑
  2. 透明无感:客户端完全不用感知代理存在
  3. 开闭原则:新增代理逻辑不修改真实业务代码
  4. 性能优化:懒加载、缓存大幅节省资源
  5. 安全隔离:权限拦截、隐藏真实对象地址与实现
  6. 统一管控:所有请求收口在代理层,便于统一治理

缺点

  1. 增加代理类,系统类数量变多
  2. 多一层调用链路,轻微性能损耗
  3. 多层代理嵌套会增加调试难度
  4. 静态代理会造成代理类泛滥

11. C++ 工程开发最佳实践规范

  1. 统一采用抽象接口 + 智能指针 实现静态代理
  2. 懒加载场景使用 mutable 修饰内部真实对象指针,保证 const 语义合法
  3. 代理类只做拦截、控制、增强,不写核心业务逻辑
  4. 优先使用组合持有真实对象,不滥用继承
  5. 通用横切逻辑(日志/耗时)封装为通用模板代理
  6. 控制代理嵌套层数,建议不超过2层
  7. 多线程懒加载使用 std::call_once 保证线程安全

12. 常见坑点与完整避坑指南

  1. 坑:代理与真实对象不统一接口
    避坑:严格继承同一抽象 Subject,保证透明替换。
  2. 坑:裸指针管理不当导致内存泄漏
    避坑:全程使用 std::unique_ptr/shared_ptr
  3. 坑:在代理中修改真实业务逻辑
    避坑:代理只做前置后置,不侵入核心逻辑。
  4. 坑:滥用多层代理嵌套
    避坑:层级越少越好,避免调用链复杂难调试。
  5. 坑:简单业务强行上代理模式
    避坑:无管控、无懒加载、无缓存需求直接调用原对象即可。

13. 面试高频深挖题(标准答案)

Q1:代理模式的核心原理与作用?

通过引入代理中间层,控制客户端对真实对象的访问,实现懒加载、权限拦截、缓存、日志、远程调用,接口保持不变,不修改真实业务代码。

Q2:静态代理和动态代理区别?

静态代理编译期手动编写,一对一代理,类易膨胀;动态代理运行时动态生成,一个代理适配多个目标类。C++无原生动态代理,可用模板模拟通用代理。

Q3:代理和装饰器最核心区别?

代理重在访问控制与隔离 ,通常自行创建真实对象;装饰器重在功能叠加增强,由外部传入被装饰对象,可无限嵌套组合。

Q4:虚拟代理解决什么问题?

解决大对象初始化开销大问题,实现延迟加载,只有真正使用时才创建初始化,节省内存与启动时间。

Q5:C++ 中哪些是代理模式的应用?

std::shared_ptr/unique_ptr 智能指针、RPC本地桩、图片懒加载、权限拦截器都是代理模式思想。


14. C++ 实际项目落地场景

  1. 网络框架:RPC客户端本地代理、请求拦截代理
  2. 配置系统:大配置文件懒加载虚拟代理
  3. 权限系统:接口访问保护代理,统一鉴权
  4. 数据查询:数据库查询缓存代理
  5. 游戏开发:大资源、模型、贴图懒加载代理
  6. 中间件:请求日志、限流、监控统一代理层
相关推荐
水木流年追梦1 小时前
大模型入门-应用篇2-RAG (检索增强生成):从原理到 Python 实战
开发语言·python·算法·prompt
froginwe111 小时前
《Foundation 提示框》详解
开发语言
handler011 小时前
速通蓝桥杯省一: 前缀和&差分(附经典例题)
c语言·c++·笔记·职场和发展·蓝桥杯
谙弆悕博士1 小时前
快速学C语言——第 11 章:指针与数组
服务器·c语言·开发语言·学习方法·业界资讯·指针·数组
无限进步_1 小时前
【C++】lambda表达式与std::function/bind包装器
开发语言·c++
树下水月1 小时前
php artisan serve 在window上执行报错的问题
开发语言·php
梦梦代码精1 小时前
电商系统的核心难点:订单与营销系统如何设计?——LikeShop 架构深度拆解(规则计算与状态一致性)
java·开发语言·低代码·架构·开源·github
样例过了就是过了1 小时前
LeetCode热题100 多数元素
c++·算法·leetcode·贪心算法
隐退山林1 小时前
JavaEE进阶:SpringBoot日志
java·开发语言