• 06 C++设计模式之代理(Proxy)模式


    代理模式定义

    代理是一种结构型设计模式,让你能够提供对象的替代品或其占位符。代理控制着对于原对象的访问,并允许在将请求提交给对象前后进行一些处理。

    代理模式优缺点

    优点

    • 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
    • 代理对象可以扩展目标对象的功能;
    • 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度,增加了程序的可扩展性

    缺点

    • 代理模式会造成系统设计中类的数量增加
    • 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
    • 增加了系统的复杂度

    代理模式构成与实现

    构成

    • 服务接口(Service Interface)声明了服务接口。代理必须遵循该接口才能伪装成服务对象。
    • 服务(Service)类提供了一些实用的业务逻辑。
    • 代理(Proxy)类包含一个指向服务对象的引用成员变量。代理完成其任务(例如延迟初始化、记录日志、访问控制和缓存等)后会将请求传递给服务对象。通常情况下,代理会对其服务对象的整个生命周期进行管理。
    • 客户端(Client) 能通过同一接口与服务或代理进行交互,所以你可在一切需要服务对象的代码中使用代理。

    实现

    ServiceInterface.h:

    #ifndef SERVICE_INTERFACE_H_
    #define SERVICE_INTERFACE_H_
    
    #include 
    
    // 远程服务接口
    class ThirdPartyTVLib {
     public:
        virtual std::string listVideos() = 0;
        virtual std::string getVideoInfo(int id) = 0;
    };
    
    #endif  // SERVICE_INTERFACE_H_
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    Service.h:

    #ifndef SERVICE_H_
    #define SERVICE_H_
    
    #include 
    #include "ServiceInterface.h"
    
    // 视频下载类
    // 该类的方法可以向远程视频后端服务请求信息, 请求速度取决于用户和服务器的网络状况
    // 如果同时发送大量请求, 即使所请求的信息一模一样, 程序的速度依然会变慢
    class ThirdPartyTVClass : public ThirdPartyTVLib {
     public:
        std::string listVideos() override {
            // 向远程视频后端服务发送一个API请求获取视频信息, 这里忽略实现
            return "video list";
        }
    
        std::string getVideoInfo(int id) override {
            // 向远程视频后端服务发送一个API请求获取某个视频的元数据, 这里忽略实现
            return "video info";
        }
    };
    
    #endif  //  SERVICE_H_
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    Proxy.h:

    #ifndef PROXY_H_
    #define PROXY_H_
    
    #include 
    #include "ServiceInterface.h"
    
    // 为了节省网络带宽, 我们可以将请求缓存下来并保存一段时间
    // 当代理类接受到真实请求后才会将其委派给服务对象
    class CachedTVClass : public ThirdPartyTVLib {
     public:
        explicit CachedTVClass(ThirdPartyTVLib* service) : service_(service), need_reset_(false), list_cache_(""), video_cache_("") {}
        void reset() {
            need_reset_ = true;
        }
    
        std::string listVideos() override {
            if (list_cache_ == "" || need_reset_) {
                list_cache_ = service_->listVideos();
            }
            return list_cache_;
        }
    
        std::string getVideoInfo(int id) override {
            if (video_cache_ == "" || need_reset_) {
                video_cache_ = service_->getVideoInfo(id);
            }
            return video_cache_;
        }
    
     private:
        ThirdPartyTVLib* service_;
        std::string list_cache_;
        std::string video_cache_;
        bool need_reset_;
    };
    
    #endif  // PROXY_H_
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    Client.h:

    #ifndef CLIENT_H_
    #define CLIENT_H_
    
    #include 
    #include 
    #include "Service.h"
    
    // 之前直接与服务对象交互的 GUI 类不需要改变, 前提是它仅通过接口与服务对象交互。
    // 我们可以安全地传递一个代理对象来代替真实服务对象, 因为它们都实现了相同的接口。
    class TVManager {
     public:
        explicit TVManager(ThirdPartyTVLib* s) : service_(s) {}
        void renderVideoPage(int id) {
            std::string video_info = service_->getVideoInfo(id);
            // 渲染视频页面, 这里忽略实现
            printf("渲染视频页面: %s\n", video_info.c_str());
            return;
        }
        void renderListPanel() {
            std::string videos = service_->listVideos();
            // 渲染视频缩略图列表, 这里忽略实现
            printf("渲染视频缩略图列表: %s\n", videos.c_str());
            return;
        }
    
     private:
        ThirdPartyTVLib* service_;
    };
    
    #endif  // CLIENT_H_
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    main.cpp

    #include "Client.h"
    #include "Service.h"
    #include "Proxy.h"
    
    int main() {
        system("chcp 65001");
        ThirdPartyTVClass* aTVService = new ThirdPartyTVClass();
        CachedTVClass* aTVProxy = new CachedTVClass(aTVService);
        TVManager* manager = new TVManager(aTVProxy);
    
        manager->renderVideoPage(1);
        manager->renderListPanel();
    
        delete aTVService;
        delete aTVProxy;
        delete manager;
        system("pause");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    输出

    Active code page: 65001
    渲染视频页面: video info
    渲染视频缩略图列表: video list
    Press any key to continue . . .
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • 相关阅读:
    SpringBoot解决跨域问题的六种方式
    【Graph Net学习】GNN/GCN代码实战
    第六章 Spring(IOC/DI)依赖注入(配置文件)
    [产品管理-2]:产品经理的职责、在企业中的位置与定位
    iwebsec靶场 SQL注入漏洞通关笔记5- updatexml注入(报错型盲注)
    extcon驱动及其在USB驱动中的应用
    Pytorch+cpp_cuda extension 课程一
    Chapter9.5:线性系统的状态空间分析与综合考研参考题
    pdf也可以制作成可翻页的电子书吗?
    Java 面试题 —— 请你介绍一下 Java 的线程池
  • 原文地址:https://blog.csdn.net/qq_45531502/article/details/125618786