C++ | 设计模式 | 代理模式

发布于:2025-02-27 ⋅ 阅读:(19) ⋅ 点赞:(0)

代理模式(Proxy Pattern)

代理模式是一种结构型设计模式 ,它为某个对象提供一个代理,以控制对该对象的访问。代理模式可以在不改变原始对象的情况下,通过引入代理对象来扩展功能或控制对原始对象的访问。

核心思想
代理模式的核心思想是通过一个代理对象来代替直接操作目标对象,从而实现对目标对象的访问控制、延迟加载、权限管理等功能。代理对象通常与目标对象实现相同的接口,因此客户端代码可以像使用目标对象一样使用代理对象,而无需感知代理的存在。
适用于需要对对象的访问进行控制或增强的场景。
通过合理使用代理模式,可以在不改变原有对象的基础上,灵活地扩展功能并提升系统的可维护性。

代理模式的分类

根据用途,代理模式可以分为以下几种类型:

远程代理(Remote Proxy)
为一个位于不同地址空间的对象提供本地代理,隐藏远程通信的细节。例如,Java RMI(远程方法调用)就是一种远程代理的实现。
虚拟代理(Virtual Proxy)
延迟创建开销较大的对象,直到真正需要时才初始化。例如,图片加载时先显示占位符,待图片下载完成后替换。
保护代理(Protection Proxy)
控制对目标对象的访问权限。例如,只有特定用户才能访问某些资源。
智能引用代理(Smart Reference Proxy)
在访问目标对象时附加额外的操作,例如引用计数、缓存、日志记录等。

代理模式的组成

Subject(抽象主题)
定义了目标对象和代理对象的共同接口,这样代理对象可以用来替代目标对象。
RealSubject(真实主题)
实现了抽象主题接口,定义了实际的业务逻辑。
Proxy(代理)
持有一个对真实主题的引用,并在需要时调用真实主题的方法。代理可以在调用前后添加额外的逻辑,比如权限检查、日志记录、延迟加载等。
Client(客户端)
使用代理对象完成对目标对象的操作。

实现

#include <iostream>
using namespace std;

class VideoSite
{
public:
    virtual void freeMovie() = 0;
    virtual void vipMovie() = 0;
    virtual void ticketMovie() = 0;
};

class FixBugVideoSite :public VideoSite
{
public:
    virtual void freeMovie() {
        cout << "free Movie" << endl;
    }
    virtual void vipMovie() {
        cout << "vip Movie" << endl;
    }
    virtual void ticketMovie() {
        cout << "ticket movie" << endl;
    }
};
class FreeVideoSiteProxy :public VideoSite
{
public:
    FreeVideoSiteProxy() {
        pVideo = new FixBugVideoSite();
    }
    ~FreeVideoSiteProxy() { delete pVideo; }
    virtual void freeMovie()
    {
        pVideo->freeMovie();
    }
    virtual void vipMovie() {
        cout<<"error"<<endl;
    }
    virtual void ticketMovie() {
        cout << "error" << endl;
    }
private:
    VideoSite* pVideo;
};
class VipVideoSiteProxy :public VideoSite
{
public:
    VipVideoSiteProxy() {
        pVideo = new FixBugVideoSite();
    }
    ~VipVideoSiteProxy() { delete pVideo; }
    virtual void freeMovie()
    {
        pVideo->freeMovie();
    }
    virtual void vipMovie() {
        pVideo->vipMovie();
    }
    virtual void ticketMovie() {
        cout << "error" << endl;
    }
private:
    VideoSite* pVideo;
};

void watch(VideoSite* p)
{
    p->freeMovie();
    p->vipMovie();
    p->ticketMovie();
}
int main()
{
    VideoSite* p1 = new FreeVideoSiteProxy();
    VideoSite* p2 = new VipVideoSiteProxy();

    watch(p1);
    watch(p2);

    std::cout << "Hello World!\n";
}

事例

场景描述
假设我们有一个 Image 类,用于加载和显示图片。由于图片加载可能需要耗费大量资源,我们可以使用虚拟代理来延迟加载图片,直到真正需要显示时才加载。

#include <iostream>
#include <memory> // 使用智能指针管理资源

// 抽象主题接口
class Image {
public:
    virtual void display() = 0; // 显示图片的接口
    virtual ~Image() = default; // 虚析构函数确保正确释放资源
};

// 真实主题类
class RealImage : public Image {
private:
    std::string fileName;

    void loadFromDisk() {
        std::cout << "加载图片: " << fileName << " 从磁盘..." << std::endl;
    }

public:
    explicit RealImage(const std::string& fileName) : fileName(fileName) {
        loadFromDisk(); // 模拟图片加载过程
    }

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

// 代理类
class ProxyImage : public Image {
private:
    std::unique_ptr<RealImage> realImage; // 使用智能指针管理真实对象
    std::string fileName;

public:
    explicit ProxyImage(const std::string& fileName) : fileName(fileName), realImage(nullptr) {}

    void display() override {
        if (!realImage) {
            realImage = std::make_unique<RealImage>(fileName); // 延迟加载
        }
        realImage->display();
    }
};

// 客户端代码
int main() {
    // 创建代理对象
    std::unique_ptr<Image> image1 = std::make_unique<ProxyImage>("photo1.jpg");
    std::unique_ptr<Image> image2 = std::make_unique<ProxyImage>("photo2.jpg");

    // 第一次调用 display 时会加载图片
    std::cout << "第一次调用:" << std::endl;
    image1->display();

    // 第二次调用 display 时不会重新加载图片
    std::cout << "\n第二次调用:" << std::endl;
    image1->display();

    // 另一个图片对象
    std::cout << "\n另一个图片对象:" << std::endl;
    image2->display();

    return 0;
}

运行结果

第一次调用:
加载图片: photo1.jpg 从磁盘...
显示图片: photo1.jpg

第二次调用:
显示图片: photo1.jpg

另一个图片对象:
加载图片: photo2.jpg 从磁盘...
显示图片: photo2.jpg

代码解析

  1. 抽象主题接口 (Image)
  • 定义了 display() 方法,作为所有图片对象的统一接口。
  • 使用虚析构函数确保子类对象能够被正确释放。
  1. 真实主题类 (RealImage)
  • 实现了 display() 方法,负责实际的图片加载和显示。
  • 在构造函数中模拟了从磁盘加载图片的过程。
  1. 代理类 (ProxyImage)
  • 持有一个指向 RealImage 的智能指针 (std::unique_ptr)。
  • 在第一次调用 display() 时创建 RealImage 对象(延迟加载)。
  • 后续调用直接复用已创建的 RealImage 对象。
  1. 客户端代码
  • 客户端通过代理对象访问图片,无需感知图片是否已经加载。

代理模式的优点(在 C++ 中体现)

  1. 资源管理优化
    延迟加载减少了不必要的资源消耗,尤其适用于大型文件或高开销对象。
  2. 代码解耦
    客户端代码与真实对象的实现完全解耦,符合开闭原则。
  3. 功能扩展
    可以轻松扩展代理类的功能,例如添加日志记录、权限检查等。