代理模式(Proxy)

news2024/12/22 6:15:10

定义

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

前言

1. 问题

举个例子:有这样一个消耗大量系统资源的巨型对象, 你只是偶尔需要使用它,并非总是需要。

你可以实现延迟初始化:在实际有需要时再创建该对象对象的所有客户端都要执行延迟初始代码。不幸的是,这很可能会带来很多重复代码。 在理想情况下,我们希望将代码直接放入对象的类中,但这并非总是能实现:比如类可能是第三方封闭库的一部分。

2. 解决方案

代理模式建议新建一个与原服务对象接口相同的代理类,然后更新应用以将代理对象传递给所有原始对象客户端。代理类接收到客户端请求后会创建实际的服务对象,并将所有工作委派给它。

代理将自己伪装成数据库对象,可在客户端或实际数据库对象不知情的情况下处理延迟初始化和缓存查询结果的工作。

这有什么好处呢?如果需要在类的主要业务逻辑前后执行一些工作,你无需修改类就能完成这项工作。由于代理实现的接口与原类相同,因此你可将其传递给任何一个使用实际服务对象的客户端。

结构

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

适用场景

  • 延迟初始化(虚拟代理)。如果你有一个偶尔使用的重量级服务对象,一直保持该对象运行会消耗系统资源时,可使用代理模式。

你无需在程序启动时就创建该对象,可将对象的初始化延迟到真正有需要的时候。

  • 访问控制(保护代理)。如果你只希望特定客户端使用服务对象,这里的对象可以是操作系统中非常重要的部分,而客户端则是各种已启动的程序(包括恶意程序),此时可使用代理模式。

代理可仅在客户端凭据满足要求时将请求传递给服务对象。

  • 本地执行远程服务(远程代理)。适用于服务对象位于远程服务器上的情形。

在这种情形中,代理通过网络传递客户端请求,负责处理所有与网络相关的复杂细节。

  • 记录日志请求(日志记录代理)。适用于当你需要保存对于服务对象的请求历史记录时。代理可以在向服务传递请求前进行记录。
  • 缓存请求结果(缓存代理)。适用于需要缓存客户请求结果并对缓存生命周期进行管理时,特别是当返回结果的体积非常大时。

代理可对重复请求所需的相同结果进行缓存,还可使用请求参数作为索引缓存的键值。

  • 智能引用。当一个对象被引用时提供一些额外的操作,比如将对象被调用的次数记录下来等。可在没有客户端使用某个重量级对象时立即销毁该对象

代理会将所有获取了指向服务对象或其结果的客户端记录在案。代理会时不时地遍历各个客户端,检查它们是否仍在运行。如果相应的客户端列表为空,代理就会销毁该服务对象,释放底层系统资源。代理还可以记录客户端是否修改了服务对象。其他客户端还可以复用未修改的对象。

实现方式

  1. 如果没有现成的服务接口,你就需要创建一个接口来实现代理和服务对象的可交换性。从服务类中抽取接口并非总是可行的,因为你需要对服务的所有客户端进行修改,让它们使用接口。备选计划是将代理作为服务类的子类,这样代理就能继承服务的所有接口了。
  2. 创建代理类,其中必须包含一个存储指向服务的引用的成员变量。通常情况下,代理负责创建服务并对其整个生命周期进行管理。在一些特殊情况下,客户端会通过构造函数将服务传递给代理。
  3. 根据需求实现代理方法。在大部分情况下,代理在完成一些任务后应将工作委派给服务对象。
  4. 可以考虑新建一个构建方法来判断客户端可获取的是代理还是实际服务。 你可以在代理类中创建一个简单的静态方法,也可以创建一个完整的工厂方法。
  5. 可以考虑为服务对象实现延迟初始化。

优点

  • 你可以在客户端毫无察觉的情况下控制服务对象。
  • 如果客户端对服务对象的生命周期没有特殊要求,你可以对生命周期进行管理。
  • 即使服务对象还未准备好或不存在,代理也可以正常工作。
  • 开闭原则。你可以在不对服务或客户端做出修改的情况下创 建新代理。

缺点

  • 代码可能会变得复杂,因为需要新建许多类。
  • 服务响应可能会延迟。

ServiceInterface.hpp

#ifndef C9D296A5_E3CD_49BA_973E_BEC89D4C1B83
#define C9D296A5_E3CD_49BA_973E_BEC89D4C1B83
#include <string>
using namespace std;
class ThirdPartyTVLib{
    public:
        virtual string listVideos() = 0;
        virtual string getVideoInfo(int id) = 0;
};

#endif /* C9D296A5_E3CD_49BA_973E_BEC89D4C1B83 */

Service.hpp

#ifndef B13EB0FC_4951_4363_BAD4_7033A6E36357
#define B13EB0FC_4951_4363_BAD4_7033A6E36357
#include "string"
#include "ServiceInterface.hpp"

class ThirdPartyTVClass : public ThirdPartyTVLib {
 public:
    std::string listVideos() override {
        // 向远程视频后端服务发送一个API请求获取视频信息, 这里忽略实现
        return "video list";
    }

    std::string getVideoInfo(int id) override {
        // 向远程视频后端服务发送一个API请求获取某个视频的元数据, 这里忽略实现
        return "video info";
    }
};

#endif /* B13EB0FC_4951_4363_BAD4_7033A6E36357 */

 Proxy.hpp

#ifndef BBF7D2AD_221E_4784_BDA3_BA134E2FC787
#define BBF7D2AD_221E_4784_BDA3_BA134E2FC787

#include <string>
#include "ServiceInterface.hpp"

// 为了节省网络带宽, 我们可以将请求缓存下来并保存一段时间
// 当代理类接受到真实请求后才会将其委派给服务对象
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 /* BBF7D2AD_221E_4784_BDA3_BA134E2FC787 */

Client.hpp

#ifndef D9959BB2_43B6_4562_99CE_1CCADDFEC5AC
#define D9959BB2_43B6_4562_99CE_1CCADDFEC5AC

#include <string>
#include <cstdio>
#include "Service.hpp"

// 之前直接与服务对象交互的 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 /* D9959BB2_43B6_4562_99CE_1CCADDFEC5AC */

main.cpp

#include "Client.hpp"
#include "Service.hpp"
#include "Proxy.hpp"

int main() {
    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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/692952.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

mac部署fastadmin踩坑记录

粘贴一下解决配置&#xff0c;主要Nginx配置问题 //后台NGINX location / {if (!-e $request_filename) {rewrite ^(.?\.php)(/.)$ /$1?s$2 last;# 加上这一句配置试试rewrite ^(.*)$ /ewgadmin.php?s$1 last; # 对应项目修改对应入口文件break;}}//接口文档Nginx配置 loca…

sql server还原新数据库,解决原库还原中...

1&#xff09;用studyDB库&#xff0c;备份出数据库备份文件 studyDB_backup_2023_06_19.bak 2&#xff09;用备份文件 studyDB_backup_2023_06_19.bak还原数据新库CollegeStudyDB和原库studyDB到同一服务器 3&#xff09;数据库CollegeStudyDB按原成功&#xff0c;但是原库s…

Linux环境准备以及CentOS7.6系统安装

&#xff08;该图由AI绘制 本人提供教学 FREE&#xff09; 运维概述与Linux系统安装 一、VMware虚拟机 1、什么是虚拟机 其实虚拟机就是在Windows的真机上创建一个独立的其他操作系统的运行环境而且其对宿主机&#xff08;Windows&#xff09;没有任何影响。 2、虚拟机的种…

《操作系统》by李治军 | 实验7 - 地址映射与共享

目录 一、实验目的 二、实验内容 &#xff08;一&#xff09;跟踪地址翻译过程 &#xff08;二&#xff09;基于共享内存的生产者—消费者程序 &#xff08;三&#xff09;共享内存的实现 三、实验准备 1. Linux 中的共享内存 2. 获得空闲物理页面 3. 地址映射 4. 寻…

TS学习笔记——模块

模块 module 模块&#xff1a;在自身的作用域里执行&#xff0c;不是在全局作用域中&#xff0c;若是外部想要使用需要导入导出。 好处&#xff1a;1、提高了代码的复用性 2、解决命名冲突 3、提高代码的可维护性 模块导入导出&#xff1a;export &#xff08;interface…

同步和异步、同步复位、异步复位、同步释放

文章目录 同步和异步同步复位、异步复位、同步释放同步复位异步复位同步释放&#xff08;异步信号和CLK信号存在时序检查、Recover time&Removel time&#xff09;典型的异步复位同步释放的verilog电路设计 同步和异步 数字电路根据逻辑功能的不同特点&#xff0c;可以分成…

如何判断端口有没有被占用

第一步&#xff1a;打开cmd 输入netstat -ano 可以查看自己的所有端口&#xff0c;第一行的本地地址是端口22&#xff0c;最右面对应的pid13260 第二步&#xff1a;打开任务管理器对应的PID就知道那个服务项占用了22端口了 第二种查看指定方法&#xff1a; netstat -ano|finds…

将数据库与LLMs结合,增强模型的长期记忆能力--ChatDB

ChatDB: AUGMENTING LLMS WITH DATABASES AS THEIR SYMBOLIC MEMORY 返回论文和资料目录 论文地址 项目地址 1.导读 清华团队针对大模型LLMs的长期记忆能力进行的改进。改进思路是将LLMs与数据库结合&#xff0c;将信息以符号化的形式存储在数据库中。同时&#xff0c;使用大…

极致呈现系列之:Echarts日历坐标系的时光流转

目录 什么是日历坐标系Echarts日历坐标系的特性Echarts日历坐标系的应用场景Echarts日历坐标系中常用的配置项Vue3中使用Echats日历坐标系实现健康可视化图表日历饼图 什么是日历坐标系 日历坐标系是一种用于展示时间数据的坐标系&#xff0c;将数据按照日期呈现在一个日历形式…

「Java核心技术大会 2023」6月重磅启动,邀你共同探讨Java生态 ~文末福利

Java核心技术大会 2023 大会简介直播预约&#xff1a;视频号“IT阅读排行榜”PART 1 特邀启动专场PART 2 Java语言、平台和趋势专场PART 3 Java应用开发专场PART 4 Java应用与系统架构专场PART 5 Java应用性能优化专场PART 6 大数据与数据库专场PART 7 云原生与Serverless专场P…

STM32学习 6月27日

51单片机中有时钟和时钟树的概念&#xff0c;外设只有GPIO、定时器、和一个串口&#xff0c;使用的都是11.0592MHZ的频率&#xff0c;除了定时器外&#xff0c;其他外设只要上电就可以使用。 stm32每个外设都有对应控制的开关&#xff0c;其所使用的频率各不相同&#xff0c;需…

[Windows] ImageGlass Kobe v8.9便携版

这款ImageGlass Kobe 神仙看图软件&#xff0c;UI漂亮&#xff0c;而且官方收费95元限时免费&#xff0c;打开大图速度极快&#xff0c;界面简洁纯净无广告&#xff0c;简直就是我的梦中情软&#xff0c;看图体验真的嘎嘎好&#xff01;是优秀的 Windows 照片查看器替代品。 具…

数据库监控与调优【十一】—— 索引调优技巧

索引调优技巧 长字段的索引调优使用组合索引的技巧覆盖索引排序优化冗余、重复索引的优化 长字段的索引调优 举例 实际项目中&#xff0c;我们可能需要给很长的字段添加索引。 比如以下first_name字段里面存储的数据普遍在200以上。 SELECT* FROMemployees WHEREfirst_…

Qt 动态手势识别“握拳”

系列文章目录 通过Qt实现手势识别控制软件操作相关系列技术方案 &#xff08;一&#xff09;Qt 将某控件、图案绘制在最前面的方法&#xff0c;通过QGraphicsScene模块实现 &#xff08;二&#xff09;Qt QGraphicsScene模块实现圆点绘制在所有窗体的最前方&#xff0c;实现圆…

永磁同步电机无位置传感器控制,采用的是龙贝格,基于模型的 定点开发,仿真效果和实际95%高度吻合

永磁同步电机无位置传感器控制&#xff0c;采用的是龙贝格&#xff0c;基于模型的 定点开发&#xff0c;仿真效果和实际95%高度吻合&#xff0c;可以仿真学习&#xff0c;也可以直接移植到项目中 YID:32768642079012761

深度学习框架背景

深度学习框架背景 本文目录&#xff1a; 一、深度学习框架概念 二、为何要用深度学习框架 三、掌握深度学习框架要做哪些准备 四、深度学习主要应用场景 五、常见深度学习框架的对比 六、深度学习框架在市场上的占比 七、中国深度学习开源框架状况 八、备注 一、深度学…

redis------Hash操作(字典)

Hash操作&#xff0c;redis中Hash在内存中的存储格式如下图&#xff1a; # hash类型就是咱们python中的字典&#xff0c;key-value,字典又叫hash类型 字典的key必须可hash -字典类型在底层存储&#xff0c;基于数组存的 key---{key:value,key:value} hset(name, key, val…

【Vue】CI持续集成Vue前端项目--20230627

我的实践&#xff08;CI&#xff09; 1.打包 npm run build&#xff0c;产生dist文件 2.将dist文件的内容拷贝到static下面。不用在nginx文件夹中 3.编写nginx配置 default.conf server {listen 80;listen [::]:80;server_name localhost;location / {root /usr/…

【MySQL数据库】MHA高可用配置及故障切换

目录 一、MHA简介1.1什么是MHA1.2MHA的组成1.3MHA的特点 二、搭建MHA2.1故障模拟2.2故障修复 一、MHA简介 1.1什么是MHA MHA&#xff08;MasterHigh Availability&#xff09;是一套优秀的MySQL高可用环境下故障切换和主从复制的软件。MHA 的出现就是解决MySQL 单点的问题。M…

掌握构造函数:打破面向对象编程难题

文章目录 I. 介绍解释构造函数的概念及其作用为什么构造函数是面向对象编程中至关重要的一部分 II. 创建构造函数介绍如何定义并创建一个构造函数着重介绍构造函数的语法和使用方法定义一个构造函数创建一个对象实例使用对象实例继承和原型 III. 构造函数的参数介绍构造函数的参…