C++设计模式:代理模式(Proxy)(附案例代码)

发布于:2024-12-06 ⋅ 阅读:(24) ⋅ 点赞:(0)

什么是代理模式?

代理模式是一种结构型设计模式,主要用于为某个对象提供一个代理,以便在不直接访问对象的情况下控制对其的访问。代理可以在客户端和目标对象之间起到一个中介的作用,添加一些额外的操作,例如权限控制、性能优化(如延迟加载)、结果缓存、日志记录等。

通过代理模式,客户端与目标对象之间的耦合被降低,同时实现了对目标对象的透明化访问。


代理模式的特点

优点

  1. 控制对象访问

    • 可以通过代理控制对目标对象的访问,比如权限检查或延迟加载。
  2. 解耦增强系统灵活性

    • 客户端只需与代理对象交互,代理负责实际的操作逻辑,隐藏了具体的实现细节。
  3. 额外功能增强

    • 在不修改目标对象代码的前提下,通过代理扩展功能,例如添加日志记录、性能优化等。

缺点

  1. 增加系统复杂性

    • 引入代理对象会增加代码量和复杂性,尤其是代理逻辑较为复杂时。
  2. 性能开销

    • 代理本身需要额外的内存和处理时间。

代理模式的使用场景

  1. 权限控制(保护代理)

    • 根据用户角色或权限控制对目标对象的访问。
    • 例如:管理员可以查看银行账户余额,而普通用户不能。
  2. 延迟加载(虚拟代理)

    • 在需要时才创建目标对象。
    • 例如:大型资源(如图像、文件)在实际使用时再加载。
  3. 远程代理(Remote Proxy)

    • 为位于不同地址空间的对象提供本地代理。
    • 例如:远程方法调用(RMI)。
  4. 日志记录或审计

    • 在目标对象操作前后添加日志记录。
  5. 性能优化(缓存代理)

    • 缓存目标对象的操作结果,避免重复计算或网络请求。
  6. 智能引用(Smart Reference Proxy)

    • 在访问目标对象时进行计数、引用管理或预处理。

案例详解

以下通过两个案例详细解读代理模式:

  1. 虚拟代理(延迟加载)
  2. 保护代理(权限控制)

案例一:虚拟代理(延迟加载)

需求背景

假设我们要开发一个图片查看器应用。加载高清图片非常耗时,为了提升应用性能,我们可以使用代理模式,在需要时才加载图片。

代码实现
  1. 定义抽象接口

首先定义一个 Image 接口,供代理对象和目标对象实现。

#include <iostream>
#include <string>
#include <memory>
using namespace std;

// 抽象图片接口
class Image {
public:
    virtual void display() = 0; // 定义显示图片的接口
    virtual ~Image() = default;
};

  1. 实现真实对象

RealImage 是需要加载的高清图片,它模拟了一个耗时的加载过程。

class RealImage : public Image {
private:
    string fileName;

    void loadFromDisk() {
        // 模拟加载图片
        cout << "Loading image: " << fileName << endl;
    }

public:
    RealImage(const string& fileName) : fileName(fileName) {
        loadFromDisk(); // 在创建时加载图片
    }

    void display() override {
        cout << "Displaying image: " << fileName << endl;
    }
};

  1. 实现代理对象

ProxyImage 是虚拟代理,只有在需要显示图片时,才创建 RealImage

class ProxyImage : public Image {
private:
    string fileName;
    unique_ptr<RealImage> realImage; // 延迟加载的真实图片对象

public:
    ProxyImage(const string& fileName) : fileName(fileName) {}

    void display() override {
        if (!realImage) {
            // 延迟加载真实图片
            realImage = make_unique<RealImage>(fileName);
        }
        realImage->display();
    }
};

  1. 客户端代码

客户端通过代理对象显示图片。

int main() {
    // 创建代理图片
    unique_ptr<Image> image = make_unique<ProxyImage>("test.jpg");

    // 第一次显示图片
    cout << "First time displaying image:" << endl;
    image->display();

    // 第二次显示图片
    cout << "Second time displaying image:" << endl;
    image->display();

    return 0;
}

运行结果
First time displaying image:
Loading image: test.jpg
Displaying image: test.jpg
Second time displaying image:
Displaying image: test.jpg
解读
  • 第一次显示时,ProxyImage 延迟加载了 RealImage,并执行加载操作。
  • 第二次显示时,直接使用已经加载好的图片,提升了性能。

案例二:保护代理(权限控制)

需求背景

实现一个银行账户系统,只有管理员用户可以查看账户余额,普通用户尝试查看时会被拒绝。

代码实现
  1. 定义接口

定义 BankAccount 接口,提供查看余额的功能。

class BankAccount {
public:
    virtual void showBalance() = 0; // 查看余额方法
    virtual ~BankAccount() = default;
};

  1. 实现真实对象

RealBankAccount 存储账户余额,并提供查看功能。

class RealBankAccount : public BankAccount {
private:
    double balance;

public:
    RealBankAccount(double initialBalance) : balance(initialBalance) {}

    void showBalance() override {
        cout << "The balance is: $" << balance << endl;
    }
};

  1. 实现代理对象

BankAccountProxy 负责检查用户角色,只有管理员可以访问 RealBankAccount

class BankAccountProxy : public BankAccount {
private:
    unique_ptr<RealBankAccount> realAccount;
    string role; // 用户角色

public:
    BankAccountProxy(double initialBalance, const string& userRole)
        : realAccount(make_unique<RealBankAccount>(initialBalance)), role(userRole) {}

    void showBalance() override {
        if (role == "admin") {
            realAccount->showBalance();
        } else {
            cout << "Access denied: Only admin can view the balance." << endl;
        }
    }
};

  1. 客户端代码

客户端通过代理访问账户信息。

int main() {
    // 普通用户
    unique_ptr<BankAccount> userAccount = make_unique<BankAccountProxy>(1000.0, "user");
    userAccount->showBalance();

    // 管理员
    unique_ptr<BankAccount> adminAccount = make_unique<BankAccountProxy>(1000.0, "admin");
    adminAccount->showBalance();

    return 0;
}

运行结果
Access denied: Only admin can view the balance.
The balance is: $1000
解读
  • 普通用户尝试查看余额时,代理拒绝访问。
  • 管理员可以通过代理访问真实账户信息。

完整代码项目

文件结构
proxy_pattern_project/
│
├── main.cpp       // 主程序入口
├── Image.h        // 图片接口及相关类
├── BankAccount.h  // 银行账户接口及相关类

1. Image.h

// Image.h
#ifndef IMAGE_H
#define IMAGE_H

#include <iostream>
#include <string>
#include <memory>
using namespace std;

// 抽象图片接口
class Image {
public:
    virtual void display() = 0; // 定义显示图片的接口
    virtual ~Image() = default;
};

// 真实图片类
class RealImage : public Image {
private:
    string fileName;

    void loadFromDisk() {
        // 模拟加载图片的过程
        cout << "加载图片:" << fileName << endl;
    }

public:
    RealImage(const string& fileName) : fileName(fileName) {
        loadFromDisk(); // 构造时加载图片
    }

    void display() override {
        cout << "显示图片:" << fileName << endl;
    }
};

// 代理图片类
class ProxyImage : public Image {
private:
    string fileName;
    unique_ptr<RealImage> realImage; // 延迟加载的真实图片对象

public:
    ProxyImage(const string& fileName) : fileName(fileName) {}

    void display() override {
        if (!realImage) {
            // 延迟加载真实图片
            realImage = make_unique<RealImage>(fileName);
        }
        realImage->display();
    }
};

#endif // IMAGE_H

2. BankAccount.h

// BankAccount.h
#ifndef BANK_ACCOUNT_H
#define BANK_ACCOUNT_H

#include <iostream>
#include <memory>
#include <string>
using namespace std;

// 抽象银行账户接口
class BankAccount {
public:
    virtual void showBalance() = 0; // 查看余额的方法
    virtual ~BankAccount() = default;
};

// 真实银行账户类
class RealBankAccount : public BankAccount {
private:
    double balance;

public:
    RealBankAccount(double initialBalance) : balance(initialBalance) {}

    void showBalance() override {
        cout << "账户余额:$" << balance << endl;
    }
};

// 银行账户代理类
class BankAccountProxy : public BankAccount {
private:
    unique_ptr<RealBankAccount> realAccount;
    string role; // 用户角色

public:
    BankAccountProxy(double initialBalance, const string& userRole)
        : realAccount(make_unique<RealBankAccount>(initialBalance)), role(userRole) {}

    void showBalance() override {
        if (role == "admin") {
            realAccount->showBalance();
        } else {
            cout << "访问被拒绝:只有管理员可以查看余额。" << endl;
        }
    }
};

#endif // BANK_ACCOUNT_H

3. main.cpp

// main.cpp
#include "Image.h"
#include "BankAccount.h"

int main() {
    cout << "====== 虚拟代理示例(延迟加载图片) ======" << endl;

    // 创建代理图片对象
    unique_ptr<Image> image = make_unique<ProxyImage>("test.jpg");

    // 第一次显示图片(触发加载)
    cout << "第一次显示图片:" << endl;
    image->display();

    // 第二次显示图片(直接显示已加载的图片)
    cout << "第二次显示图片:" << endl;
    image->display();

    cout << "\n====== 保护代理示例(银行账户访问控制) ======" << endl;

    // 普通用户访问
    unique_ptr<BankAccount> userAccount = make_unique<BankAccountProxy>(1000.0, "user");
    cout << "普通用户尝试查看余额:" << endl;
    userAccount->showBalance();

    // 管理员访问
    unique_ptr<BankAccount> adminAccount = make_unique<BankAccountProxy>(1000.0, "admin");
    cout << "管理员查看余额:" << endl;
    adminAccount->showBalance();

    return 0;
}

示例输出

虚拟代理示例

====== 虚拟代理示例(延迟加载图片) ======
第一次显示图片:
加载图片:test.jpg
显示图片:test.jpg
第二次显示图片:
显示图片:test.jpg

保护代理示例

====== 保护代理示例(银行账户访问控制) ======
普通用户尝试查看余额:
访问被拒绝:只有管理员可以查看余额。
管理员查看余额:
账户余额:$1000


网站公告

今日签到

点亮在社区的每一天
去签到