代理模式

126 阅读5分钟

1. 概述

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

通俗来说:

代理模式可以帮助你在访问对象时加入一些额外的功能或控制,让你更加方便地管理和操作对象。就好像有个助手帮你处理一些事情,让你更轻松地完成任务一样。

2. 问题

为什么要控制某个对象的访问呢?举个例子:有这样一个消耗大量系统资源的巨型对象,你只是偶尔需要使用他,并非总是需要

image-20240612195918771

你可以实现延迟初始化:在实际有需要时在创建对象。对象的所有客户端都要执行延迟初始化代码。不幸的是,这很可能会带来很多重复代码

3. 解决方案

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

image-20240612200258830

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

4. 代理模式结构

image-20240612200438373

5.使用场景

  • 延迟初始化(虚拟代理):你无需在程序启动时就创建该对象, 可将对象的初始化延迟到真正有需要的时候。
  • 访问控制(保护代理):代理可仅在客户端凭据满足要求时将请求传递给服务对象。
  • 本地执行远程服务(远程代理): 在这种情形中, 代理通过网络传递客户端请求, 负责处理所有与网络相关的复杂细节。 适用于服务对象位于远程服务器上的情形。
  • 记录日志请求(日志记录代理):代理可以在向服务传递请求前进行记录。
  • 缓存请求结果(缓存代理): 代理可对重复请求所需的相同结果进行缓存, 还可使用请求参数作为索引缓存的键值。
  • 智能引用:可在没有客户端使用某个重量级对象时立即销毁该对象。

6. 实现方式以及代码实例

实现代理模式通常包括以下步骤:

  1. 定义接口:首先,你需要定义一个接口,它规定了代理类和真实对象都必须实现的方法。
  2. 创建服务对象:然后,你需要创建一个服务对象类,实现了上述接口,完成实际的业务逻辑。
  3. 创建代理类:接着,创建一个代理类,也实现了接口,并持有一个对服务对象的引用。代理类的方法通常会在调用服务对象之前或之后执行一些额外的操作,如权限检查、日志记录、缓存等。
  4. 使用代理类:在应用程序中,通过代理类来访问真实服务对象,而不是直接访问它。这样,代理就控制了对真实对象的访问,并且可以在访问前后进行一些处理。
  5. 根据需求扩展:根据具体需求,你可以对代理类进行扩展,添加更多的功能,例如添加缓存、延迟加载、安全检查等。

本例演示如何使用代理模式在第三方腾讯视频 (TencentVideo, 代码示例中记为 TV) 程序库中添加延迟初始化和缓存。

image-20240612201413958

 // 远程服务接口。
 interface ThirdPartyTVLib {
     // 列出视频列表
     void listVideos();
 ​
     // 获取指定视频的信息
     void getVideoInfo(String id);
 ​
     // 下载指定视频
     void downloadVideo(String id);
 }
 ​
 // 服务连接器的具体实现。
 class ThirdPartyTVClass implements ThirdPartyTVLib {
     void listVideos() {
         // 向腾讯视频发送一个 API 请求,获取视频列表信息。
     }
 ​
     void getVideoInfo(String id) {
         // 向腾讯视频发送一个 API 请求,获取指定视频的元数据。
     }
 ​
     void downloadVideo(String id) {
         // 从腾讯视频下载指定视频文件。
     }
 }
 ​
 // 代理类实现服务接口并添加缓存功能。
 class CachedTVClass implements ThirdPartyTVLib {
     private ThirdPartyTVLib service; // 实际的服务对象
     private Map<String, String> listCache; // 视频列表的缓存
     private Map<String, String> videoCache; // 视频信息的缓存
     private boolean needReset; // 是否需要重置缓存
 ​
     CachedTVClass(ThirdPartyTVLib service) {
         this.service = service;
     }
 ​
     void listVideos() {
         if (listCache == null || needReset) {
             // 如果列表缓存为空或需要重置,则向实际服务对象请求视频列表,并进行缓存。
             listCache = service.listVideos();
         }
         return listCache;
     }
 ​
     void getVideoInfo(String id) {
         if (videoCache == null || needReset) {
             // 如果视频信息缓存为空或需要重置,则向实际服务对象请求指定视频信息,并进行缓存。
             videoCache = service.getVideoInfo(id);
         }
         return videoCache;
     }
 ​
     void downloadVideo(String id) {
         if (!downloadExists(id) || needReset) {
             // 如果视频文件不存在或需要重置,则向实际服务对象下载指定视频文件。
             service.downloadVideo(id);
         }
     }
 }
 ​
 // 客户端使用代理来调用服务。
 class Application {
     void init() {
         // 创建实际的远程服务对象
         ThirdPartyTVLib aTVService = new ThirdPartyTVClass();
         // 创建代理对象,并传入实际服务对象
         ThirdPartyTVLib aTVProxy = new CachedTVClass(aTVService);
         // 创建应用管理器,并传入代理对象
         TVManager manager = new TVManager(aTVProxy);
         // 初始化应用管理器,开始处理用户输入
         manager.reactOnUserInput();
     }
 }
 ​