代理模式(Proxy Pattern)

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 代理模式(Proxy Pattern)是一种设计模式,通过一个中间对象(代理)来间接访问目标对象,以控制访问权限或添加额外功能。常见的代理类型包括远程代理、虚拟代理、保护代理和智能引用代理。代理模式常用于延迟加载、权限控制、日志记录等场景,能够提高系统的灵活性和安全性。

代理模式(Proxy Pattern)详解

定义

代理模式可以看作委托别人去办事。如果你想办一件事(比如访问一个资源、操作一个文件),但因为种种原因不能直接去做,就可以通过一个中间人来帮忙。这种中间人就是代理,既可以帮你完成任务,又可以加点“附加服务”,比如检查你的权限、记录操作日志等。


生活中的例子

  1. 公司前台
    假设你要找某位公司高管谈事情,你不能直接冲进高管办公室,而是要先经过前台。前台会判断你有没有预约,或者是否符合要求。前台就相当于代理,高管则是真实对象
  2. 明星经纪人
    你想请一位明星参加活动,不会直接联系明星,而是通过经纪人。经纪人会筛选请求、谈价格,最后安排明星工作。这里,经纪人是代理,明星是真实对象
  3. 网购快递
    当你网购时,快递公司是你的代理。你买的东西最终是由快递送到你手上,商家并不直接交货给你,而是通过快递完成的。

为什么需要代理模式?

代理模式的核心在于控制访问和扩展功能。以下是常见需求:

  1. 控制访问权限:比如你不能直接访问数据库,需要验证身份。
  2. 优化性能:有些资源初始化很慢,通过代理延迟加载可以节省性能。
  3. 增加功能:可以在代理中加入日志、统计、监控等功能。

代理模式的分类

  1. 远程代理(Remote Proxy)
    让客户端像访问本地对象一样访问远程对象。例如:RPC(远程过程调用)。
  2. 虚拟代理(Virtual Proxy)
    延迟创建耗资源的对象,按需加载。例如:图片加载器在图片未加载时显示占位符。
  3. 保护代理(Protection Proxy)
    控制对对象的访问权限。例如:文件操作时的权限检查。
  4. 智能引用代理(Smart Reference Proxy)
    在对象访问时增加额外功能,例如记录访问日志或统计引用次数。

代理模式的优点和缺点

优点

  1. 单一职责:将真实对象的核心功能与附加功能分离。
  2. 灵活性强:可以动态添加功能,如权限控制、日志记录。
  3. 资源管理:可以有效管理复杂对象的生命周期(如延迟加载)。

缺点

  1. 代码复杂性增加:引入代理后,代码结构更复杂。
  2. 性能开销:代理模式引入了额外的操作,可能会影响性能。

使用场景

序号 场景 具体案例描述 作用与目的
1 延迟加载 图片查看器延迟加载高清图片,先显示占位符。 减少初始页面加载时间,提高用户体验。
2 远程调用 分布式系统中访问远程服务时,通过代理简化调用。 降低远程服务调用的复杂性,提高代码的可维护性和可扩展性。
3 权限控制 用户权限不足时,通过代理阻止对敏感资源的直接访问。 增强系统的安全性,确保只有授权用户才能访问敏感资源。
4 监控与日志 为真实对象的每次操作记录日志,用于分析和调试。 便于追踪系统行为,分析问题原因,优化系统性能。
5 事务管理 数据库操作时,代理处理事务的提交与回滚。 确保数据库操作的原子性、一致性、隔离性和持久性(ACID属性),维护数据的完整性。

更详细的实现步骤

  1. 定义抽象接口(Subject):所有请求的公共接口。
  2. 实现真实对象(RealSubject):负责处理具体的业务逻辑。
  3. 实现代理对象(Proxy)
  • 保存一个对真实对象的引用。
  • 在真实对象的方法调用前后,添加权限检查、日志等附加功能。
  1. 客户端通过代理访问真实对象

更通俗的代码说明

C++ 示例:图片延迟加载代理

#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 << "Loading " << fileName << " from disk..." << endl;
   }

public:
   RealImage(const string& fileName) : fileName(fileName) {
       loadFromDisk();
   }

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

// 代理图片类
class ProxyImage : public Image {
private:
   unique_ptr<RealImage> realImage;
   string fileName;

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

   void display() override {
       if (!realImage) {
           realImage = make_unique<RealImage>(fileName);
       }
       realImage->display();
   }
};

// 客户端代码
int main() {
   unique_ptr<Image> image = make_unique<ProxyImage>("test.jpg");
   cout << "Image will not load until display() is called:" << endl;
   image->display(); // 第一次调用会加载图片
   image->display(); // 后续调用直接显示,不重新加载
   return 0;
}

C# 示例:远程代理模拟

using System;

// 抽象接口
public interface IImage {
   void Display();
}

// 真实图片类
public class RealImage : IImage {
   private string FileName;

   public RealImage(string fileName) {
       FileName = fileName;
       LoadFromDisk();
   }

   private void LoadFromDisk() {
       Console.WriteLine($"Loading {FileName} from disk...");
   }

   public void Display() {
       Console.WriteLine($"Displaying {FileName}");
   }
}

// 代理图片类
public class ProxyImage : IImage {
   private RealImage _realImage;
   private string FileName;

   public ProxyImage(string fileName) {
       FileName = fileName;
   }

   public void Display() {
       if (_realImage == null) {
           _realImage = new RealImage(FileName);
       }
       _realImage.Display();
   }
}

// 客户端代码
class Program {
   static void Main(string[] args) {
       IImage image = new ProxyImage("test.jpg");
       Console.WriteLine("Image will not load until Display() is called:");
       image.Display(); // 第一次调用加载图片
       image.Display(); // 后续调用不再加载
   }
}


代理模式的类图


总结

  1. 定义核心:代理为真实对象提供了一个间接访问的方式,方便控制权限或扩展功能。
  2. 实现灵活:代码可以根据需要添加日志、延迟加载或权限控制等功能。
  3. 理解方法:通过生活例子结合实际代码,代理模式的作用更加直观。
相关实践学习
【涂鸦即艺术】基于云应用开发平台CAP部署AI实时生图绘板
【涂鸦即艺术】基于云应用开发平台CAP部署AI实时生图绘板
相关文章
|
设计模式 JSON 架构师
你真的需要防腐层吗?DDD 系统间的7种关系梳理与实践
当提到系统间交互的时候,人们都会想到大名鼎鼎的防腐层,用来防止其他系统的模型变更对本系统造成影响。但是在实践这个模式的过程中,我们常常会遇到问题。此时我们也应该考虑下其他的系统交互方式。
27783 12
你真的需要防腐层吗?DDD 系统间的7种关系梳理与实践
|
11月前
|
设计模式 C# C++
适配器模式(Adapter Pattern)
适配器模式是一种结构型设计模式,通过将一个类的接口转换为客户期望的另一个接口,使原本接口不兼容的类可以一起工作。它包括目标接口、适配者和适配器三个核心角色。适配器模式常用于解决旧系统兼容性问题、第三方库整合和统一接口等场景。该模式有类适配器和对象适配器两种实现方式,分别通过继承和组合实现。适配器模式的优点包括提高兼容性、遵循开闭原则和灵活性高,但也存在适配器数量增加导致复杂性和可能影响性能的缺点。
|
7月前
|
缓存 安全 Java
【Java并发】【ConcurrentHashMap】适合初学体质的ConcurrentHashMap入门
ConcurrentHashMap是Java中线程安全的哈希表实现,支持高并发读写操作。相比Hashtable,它通过分段锁(JDK1.7)或CAS+synchronized(JDK1.8)实现更细粒度锁控制,提升性能与安全性。本文详细介绍其构造方法、添加/获取/删除元素等常用操作,并对比JDK1.7和1.8的区别,帮助开发者深入理解与使用ConcurrentHashMap。欢迎关注,了解更多!
392 5
【Java并发】【ConcurrentHashMap】适合初学体质的ConcurrentHashMap入门
|
7月前
|
机器学习/深度学习 人工智能 自然语言处理
人工智能应用领域有哪些
本文全面探讨了人工智能(AI)的应用领域和技术核心,涵盖医疗、交通、金融、教育、制造、零售等多个行业,并分析了AI技术的局限性及规避策略。同时,介绍了生成式人工智能认证项目的意义与展望。尽管AI发展面临数据依赖和算法可解释性等问题,但通过优化策略和经验验证,可推动其健康发展。未来,AI将在更多领域发挥重要作用,助力社会进步。
|
11月前
|
设计模式 Java 程序员
【23种设计模式·全精解析 | 概述篇】设计模式概述、UML图、软件设计原则
本系列文章聚焦于面向对象软件设计中的设计模式,旨在帮助开发人员掌握23种经典设计模式及其应用。内容分为三大部分:第一部分介绍设计模式的概念、UML图和软件设计原则;第二部分详细讲解创建型、结构型和行为型模式,并配以代码示例;第三部分通过自定义Spring的IOC功能综合案例,展示如何将常用设计模式应用于实际项目中。通过学习这些内容,读者可以提升编程能力,提高代码的可维护性和复用性。
2244 1
【23种设计模式·全精解析 | 概述篇】设计模式概述、UML图、软件设计原则
|
缓存 NoSQL Java
RedisTemplate操作Redis,这一篇文章就够了
redis是一款开源的Key-Value数据库,运行在内存中,由C语言编写。企业开发通常采用Redis来实现缓存。同类的产品还有memcache 、memcached 等。
3029 1
|
10月前
|
存储 Cloud Native Java
Windows下Minio的安装以及基本使用
MinIO 是一个开源的云原生分布式对象存储系统,兼容亚马逊S3接口,适合存储大容量非结构化数据。本文介绍Windows下MinIO的安装与基本使用:通过以上步骤,您可以在Windows环境中成功安装并使用MinIO。
6335 18
springboot静态资源目录访问,及自定义静态资源路径,index页面的访问
本文介绍了Spring Boot中静态资源的访问位置、如何进行静态资源访问测试、自定义静态资源路径和静态资源请求映射,以及如何处理自定义静态资源映射对index页面访问的影响。提供了两种解决方案:取消自定义静态资源映射或编写Controller来截获index.html的请求并重定向。
springboot静态资源目录访问,及自定义静态资源路径,index页面的访问
|
Java Maven Spring
如何在idea中创建Springboot项目? 手把手带你创建Springboot项目,稳!
文章详细介绍了在IDEA中创建Spring Boot项目的过程,包括选择Spring Initializr、配置项目属性、选择Spring Boot版本、导入依赖、等待依赖下载以及项目结构简介。
13378 1
|
存储 前端开发 Java
Java后端如何进行文件上传和下载 —— 本地版(文末配绝对能用的源码,超详细,超好用,一看就懂,博主在线解答) 文件如何预览和下载?(超简单教程)
本文详细介绍了在Java后端进行文件上传和下载的实现方法,包括文件上传保存到本地的完整流程、文件下载的代码实现,以及如何处理文件预览、下载大小限制和运行失败的问题,并提供了完整的代码示例。
4770 2