设计模式-代理模式

1. 代理模式概念

代理模式是一种结构型设计模式,为其他对象提供一种代理以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介作用,可以用于:

  • 控制对目标对象的访问

  • 增强目标对象的功能

  • 延迟目标对象的创建和初始化

2. 代理模式的主要类型

类型 描述 应用场景
虚拟代理 延迟创建开销大的对象 图片懒加载、大文件延迟加载
保护代理 控制访问权限 权限验证、访问控制
远程代理 为远程对象提供本地代表 RPC调用、网络服务
智能引用 在访问对象时添加额外操作 引用计数、日志记录
缓存代理 为开销大的运算结果提供缓存 计算缓存、数据库查询缓存

3. UML 结构

复制代码
┌─────────────┐       ┌─────────────┐
│   Subject   │       │   Real      │
│  +request() │<─────>│  Subject    │
└─────────────┘       │  +request() │
       △              └─────────────┘
       │                     △
       │                     │
┌─────────────┐       ┌─────────────┐
│    Proxy    │──────>│  Client     │
│  +request() │       │             │
└─────────────┘       └─────────────┘

4. C++ 代码示例

示例1:虚拟代理(图片加载)
cpp 复制代码
#include <iostream>
#include <string>
#include <memory>
#include <thread>
#include <chrono>

// 1. 抽象主题接口
class Image {
public:
    virtual ~Image() = default;
    virtual void display() = 0;
};

// 2. 真实主题
class RealImage : public Image {
private:
    std::string filename;
    
    void loadFromDisk() {
        std::cout << "正在从磁盘加载图片: " << filename << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(2)); // 模拟耗时操作
        std::cout << "图片加载完成!" << std::endl;
    }
    
public:
    RealImage(const std::string& filename) : filename(filename) {
        loadFromDisk();
    }
    
    void display() override {
        std::cout << "显示图片: " << filename << std::endl;
    }
};

// 3. 代理类
class ProxyImage : public Image {
private:
    std::string filename;
    std::unique_ptr<RealImage> realImage; // 延迟加载
    
public:
    ProxyImage(const std::string& filename) 
        : filename(filename), realImage(nullptr) {}
    
    void display() override {
        // 延迟初始化:只在真正需要时才创建真实对象
        if (realImage == nullptr) {
            realImage = std::make_unique<RealImage>(filename);
        }
        realImage->display();
    }
};

// 客户端代码
int main() {
    std::cout << "=== 虚拟代理示例(图片懒加载)===" << std::endl;
    
    // 创建代理,此时不会立即加载图片
    ProxyImage proxy("high_resolution_photo.jpg");
    
    std::cout << "\n1. 第一次调用display():" << std::endl;
    proxy.display(); // 此时才会真正加载图片
    
    std::cout << "\n2. 第二次调用display():" << std::endl;
    proxy.display(); // 直接显示,不再重新加载
    
    return 0;
}
示例2:保护代理(访问控制)
cpp 复制代码
#include <iostream>
#include <string>
#include <memory>

// 1. 抽象主题接口
class Database {
public:
    virtual ~Database() = default;
    virtual void query(const std::string& sql) = 0;
};

// 2. 真实主题
class RealDatabase : public Database {
public:
    void query(const std::string& sql) override {
        std::cout << "执行SQL查询: " << sql << std::endl;
        // 实际数据库操作...
    }
};

// 3. 代理类(保护代理)
class DatabaseProxy : public Database {
private:
    std::unique_ptr<RealDatabase> realDb;
    std::string userRole;
    
    bool hasPermission(const std::string& sql) {
        // 检查SQL语句类型
        std::string upperSql = sql;
        for (char& c : upperSql) c = toupper(c);
        
        if (upperSql.find("DELETE") != std::string::npos ||
            upperSql.find("DROP") != std::string::npos ||
            upperSql.find("UPDATE") != std::string::npos) {
            return userRole == "admin";
        }
        return true; // SELECT 语句允许所有用户
    }
    
public:
    DatabaseProxy(const std::string& role) 
        : realDb(std::make_unique<RealDatabase>()), userRole(role) {}
    
    void query(const std::string& sql) override {
        if (hasPermission(sql)) {
            std::cout << "[权限验证通过] ";
            realDb->query(sql);
        } else {
            std::cout << "[权限拒绝] 用户'" << userRole 
                      << "'无权执行此操作: " << sql << std::endl;
        }
    }
    
    void setRole(const std::string& role) {
        userRole = role;
    }
};

// 客户端代码
int main() {
    std::cout << "=== 保护代理示例(数据库权限控制)===" << std::endl;
    
    // 普通用户
    DatabaseProxy userProxy("user");
    std::cout << "\n1. 普通用户操作:" << std::endl;
    userProxy.query("SELECT * FROM users");
    userProxy.query("DELETE FROM users WHERE id=1");
    
    // 管理员
    DatabaseProxy adminProxy("admin");
    std::cout << "\n2. 管理员操作:" << std::endl;
    adminProxy.query("DELETE FROM users WHERE id=1");
    
    // 动态切换角色
    std::cout << "\n3. 动态切换角色:" << std::endl;
    DatabaseProxy dynamicProxy("user");
    dynamicProxy.query("DROP TABLE users");
    dynamicProxy.setRole("admin");
    dynamicProxy.query("DROP TABLE users");
    
    return 0;
}
示例3:智能引用代理(带缓存)
cpp 复制代码
#include <iostream>
#include <string>
#include <unordered_map>
#include <memory>
#include <cmath>
#include <chrono>

// 1. 抽象主题接口
class MathService {
public:
    virtual ~MathService() = default;
    virtual double compute(int value) = 0;
};

// 2. 真实主题(计算密集型操作)
class RealMathService : public MathService {
public:
    double compute(int value) override {
        // 模拟耗时计算
        std::cout << "正在计算 " << value << " 的平方根..." << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
        return sqrt(static_cast<double>(value));
    }
};

// 3. 代理类(带缓存)
class CachedMathService : public MathService {
private:
    std::unique_ptr<RealMathService> realService;
    std::unordered_map<int, double> cache;
    int cacheHits = 0;
    int totalRequests = 0;
    
public:
    CachedMathService() 
        : realService(std::make_unique<RealMathService>()) {}
    
    double compute(int value) override {
        totalRequests++;
        
        // 检查缓存
        auto it = cache.find(value);
        if (it != cache.end()) {
            cacheHits++;
            std::cout << "[缓存命中] ";
            return it->second;
        }
        
        // 计算并缓存结果
        std::cout << "[缓存未命中] ";
        double result = realService->compute(value);
        cache[value] = result;
        
        return result;
    }
    
    void showStats() const {
        std::cout << "\n=== 缓存统计 ===" << std::endl;
        std::cout << "总请求数: " << totalRequests << std::endl;
        std::cout << "缓存命中数: " << cacheHits << std::endl;
        std::cout << "缓存命中率: " 
                  << (totalRequests > 0 ? (100.0 * cacheHits / totalRequests) : 0)
                  << "%" << std::endl;
        std::cout << "缓存大小: " << cache.size() << std::endl;
    }
    
    void clearCache() {
        cache.clear();
        cacheHits = 0;
        totalRequests = 0;
        std::cout << "缓存已清空" << std::endl;
    }
};

// 客户端代码
int main() {
    std::cout << "=== 缓存代理示例(计算缓存)===" << std::endl;
    
    CachedMathService mathService;
    
    // 第一次计算
    std::cout << "\n1. 第一次计算相同数值:" << std::endl;
    std::cout << "结果: " << mathService.compute(16) << std::endl;
    std::cout << "结果: " << mathService.compute(16) << std::endl;
    std::cout << "结果: " << mathService.compute(16) << std::endl;
    
    // 计算不同数值
    std::cout << "\n2. 计算不同数值:" << std::endl;
    for (int i = 1; i <= 5; i++) {
        std::cout << "sqrt(" << i << ") = " << mathService.compute(i) << std::endl;
    }
    
    // 再次计算
    std::cout << "\n3. 再次计算相同数值:" << std::endl;
    for (int i = 1; i <= 5; i++) {
        std::cout << "sqrt(" << i << ") = " << mathService.compute(i) << std::endl;
    }
    
    // 显示统计信息
    mathService.showStats();
    
    return 0;
}

5. 代理模式的优缺点

优点:

  • 职责清晰:真实主题只需关注核心业务逻辑

  • 高扩展性:可以在不修改真实主题的情况下增加功能

  • 安全控制:保护代理可以控制对真实主题的访问权限

  • 性能优化:虚拟代理可以延迟加载,缓存代理可以减少重复计算

相关推荐
fpcc1 小时前
跟我学C++中级篇——Linux中文件和链接及重定向
linux·c++
Fcy6482 小时前
C++ set&&map的模拟实现
开发语言·c++·stl
reddingtons8 小时前
【游戏宣发】PS “生成式扩展”流,30秒无损适配全渠道KV
游戏·设计模式·新媒体运营·prompt·aigc·教育电商·游戏美术
fpcc9 小时前
C++编程实践——链式调用的实践
c++
sxlishaobin10 小时前
设计模式之桥接模式
java·设计模式·桥接模式
bkspiderx11 小时前
C++中的volatile:从原理到实践的全面解析
开发语言·c++·volatile
君义_noip13 小时前
信息学奥赛一本通 2134:【25CSPS提高组】道路修复 | 洛谷 P14362 [CSP-S 2025] 道路修复
c++·算法·图论·信息学奥赛·csp-s
liulilittle13 小时前
OPENPPP2 Code Analysis One
网络·c++·网络协议·信息与通信·通信
Morwit13 小时前
*【力扣hot100】 647. 回文子串
c++·算法·leetcode
天赐学c语言13 小时前
1.7 - 删除排序链表中的重要元素II && 哈希冲突常用解决冲突方法
数据结构·c++·链表·哈希算法·leecode