设计模式(十三)结构型:代理模式详解

发布于:2025-07-28 ⋅ 阅读:(12) ⋅ 点赞:(0)

设计模式(十三)结构型:代理模式详解

代理模式(Proxy Pattern)是 GoF 23 种设计模式中的结构型模式之一,其核心价值在于为其他对象提供一种间接访问的机制,以控制对原始对象的访问。它通过引入一个“代理”对象,作为客户端与真实对象之间的中介,从而在不改变原始接口的前提下,实现访问控制、延迟初始化、日志记录、权限校验、缓存、远程通信等附加功能。代理模式是实现“开闭原则”和“单一职责原则”的重要手段,广泛应用于远程服务调用(RMI、Web Service)、虚拟代理(延迟加载)、保护代理(权限控制)、智能引用(资源管理)等场景,是构建安全、高效、可维护系统的关键架构模式。

一、详细介绍

代理模式解决的是“直接访问目标对象存在限制或需要增强控制”的问题。在某些情况下,客户端不能或不应直接访问真实对象,例如:

  • 对象创建代价高昂(如大型图像、数据库连接),需延迟加载;
  • 对象位于远程主机,需通过网络访问;
  • 对象涉及敏感操作,需进行权限验证;
  • 需要监控对象的访问行为(如调用次数、执行时间)。

代理模式通过一个与真实对象具有相同接口的代理对象,拦截所有对真实对象的请求,并在转发前或后执行额外逻辑。客户端通过代理与真实对象交互,整个过程对客户端透明。

该模式包含以下核心角色:

  • Subject(抽象主题):定义真实对象和代理对象的公共接口,客户端通过该接口访问目标。可以是接口或抽象类。
  • RealSubject(真实主题):实现 Subject 接口,是代理所代表的真实对象,包含核心业务逻辑。
  • Proxy(代理类):实现 Subject 接口,持有对 RealSubject 的引用。它控制对真实对象的访问,可在调用前后执行额外操作(如检查权限、缓存结果、记录日志)。

根据使用目的不同,代理模式可分为多种类型:

  1. 远程代理(Remote Proxy):为位于不同地址空间的对象提供本地代表,隐藏网络通信细节(如 RMI、gRPC Stub)。
  2. 虚拟代理(Virtual Proxy):延迟创建开销大的对象,直到真正需要时才初始化(如图片懒加载)。
  3. 保护代理(Protection Proxy):控制对对象的访问权限,根据角色决定是否允许操作(如管理员 vs 普通用户)。
  4. 智能引用(Smart Reference):在访问对象时执行额外操作,如引用计数、空指针检查、缓存结果。
  5. 缓存代理(Caching Proxy):缓存真实对象的操作结果,提高性能,避免重复计算或远程调用。

代理模式的关键优势:

  • 增强控制:可在访问前后插入逻辑,实现横切关注点。
  • 解耦客户端与真实对象:客户端不依赖具体实现,便于替换或扩展。
  • 提高安全性:通过保护代理实现权限隔离。
  • 优化性能:通过虚拟代理延迟加载,缓存代理减少重复操作。

与“装饰器模式”相比,代理关注访问控制,装饰器关注功能增强;代理通常不改变对象行为本质,而是控制何时、如何访问;装饰器则明确添加新功能。与“外观模式”相比,代理封装的是单个对象的访问,外观封装的是多个子系统的协作

二、代理模式的UML表示

以下是代理模式的标准 UML 类图:

implements
implements
has a
uses
«interface»
Subject
+request()
RealSubject
+request()
Proxy
-realSubject: RealSubject
+request()
+preRequest()
+postRequest()
Client
-subject: Subject
+doWork()

图解说明

  • Subject 是统一接口,客户端通过它与真实对象或代理交互。
  • RealSubject 是真实业务对象。
  • Proxy 持有 RealSubject 引用,在 request() 中可调用 preRequest()postRequest() 执行额外逻辑。
  • 客户端通过 Subject 接口调用,无法区分是代理还是真实对象。

三、一个简单的Java程序实例及其UML图

以下是一个文档管理系统中“受保护的文件访问”示例,展示如何使用保护代理控制对敏感文件的访问。

Java 程序实例
// 抽象主题:文件访问接口
interface Document {
    void open();
    void edit();
}

// 真实主题:实际文档对象
class RealDocument implements Document {
    private String fileName;

    public RealDocument(String fileName) {
        this.fileName = fileName;
        loadFromDisk(); // 模拟耗时操作
    }

    private void loadFromDisk() {
        System.out.println("📄 正在从磁盘加载文档: " + fileName);
        try {
            Thread.sleep(1000); // 模拟加载延迟
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        System.out.println("✅ 文档 " + fileName + " 加载完成");
    }

    @Override
    public void open() {
        System.out.println("🔓 打开文档: " + fileName);
    }

    @Override
    public void edit() {
        System.out.println("✍️ 编辑文档: " + fileName);
    }
}

// 代理类:保护代理,控制文档访问权限
class ProtectedDocumentProxy implements Document {
    private String fileName;
    private RealDocument realDocument; // 延迟初始化
    private String currentUser;
    private boolean isAdmin;

    public ProtectedDocumentProxy(String fileName, String currentUser, boolean isAdmin) {
        this.fileName = fileName;
        this.currentUser = currentUser;
        this.isAdmin = isAdmin;
    }

    @Override
    public void open() {
        if (canAccess()) {
            // 虚拟代理:延迟加载真实对象
            if (realDocument == null) {
                realDocument = new RealDocument(fileName);
            }
            logAccess("open");
            realDocument.open();
        } else {
            System.out.println("❌ 用户 " + currentUser + " 无权打开文档: " + fileName);
        }
    }

    @Override
    public void edit() {
        if (canModify()) {
            if (realDocument == null) {
                realDocument = new RealDocument(fileName);
            }
            logAccess("edit");
            realDocument.edit();
        } else {
            System.out.println("❌ 用户 " + currentUser + " 无权编辑文档: " + fileName);
        }
    }

    // 权限检查:读取权限
    private boolean canAccess() {
        return isAdmin || currentUser.equals("owner");
    }

    // 权限检查:修改权限
    private boolean canModify() {
        return isAdmin; // 只有管理员可编辑
    }

    // 访问日志
    private void logAccess(String operation) {
        System.out.println("📝 日志: 用户 [" + currentUser + "] 执行 [" + operation + "] 操作 on " + fileName);
    }
}

// 客户端使用示例
public class ProxyPatternDemo {
    public static void main(String[] args) {
        System.out.println("🔐 文档管理系统 - 保护代理示例\n");

        // 创建代理对象(不立即加载真实文档)
        Document doc = new ProtectedDocumentProxy("财务报告.docx", "alice", false);

        // 普通用户尝试打开文档
        System.out.println("👉 用户 alice (普通用户) 尝试打开文档:");
        doc.open(); // 允许打开

        System.out.println("\n👉 用户 alice 尝试编辑文档:");
        doc.edit(); // 拒绝编辑

        System.out.println("\n" + "=".repeat(50) + "\n");

        // 管理员访问
        Document adminDoc = new ProtectedDocumentProxy("财务报告.docx", "admin", true);
        System.out.println("👉 用户 admin (管理员) 尝试打开并编辑文档:");
        adminDoc.open();
        adminDoc.edit();

        System.out.println("\n💡 说明:真实文档仅在首次访问时加载,且权限由代理控制。");
    }
}
实例对应的UML图(简化版)
implements
implements
creates on demand
uses
«interface»
Document
+open()
+edit()
RealDocument
-fileName: String
+RealDocument(fileName: String)
+open()
+edit()
ProtectedDocumentProxy
-fileName: String
-realDocument: RealDocument
-currentUser: String
-isAdmin: boolean
+open()
+edit()
+canAccess()
+canModify()
+logAccess(operation: String)
Client
+main(args: String[])

运行说明

  • ProtectedDocumentProxy 实现了 Document 接口,持有文件名和用户信息。
  • 真实文档 RealDocument 在首次 open()edit() 时才创建(虚拟代理特性)。
  • 代理在调用前检查权限(保护代理),并记录访问日志(智能引用)。
  • 客户端通过统一接口操作,无法感知代理的存在。

四、总结

特性 说明
核心目的 控制对对象的访问,增强安全性与灵活性
实现机制 实现相同接口,持有真实对象引用,拦截并转发请求
优点 访问控制、延迟加载、日志监控、远程透明化、解耦客户端
缺点 增加系统复杂性、可能引入性能开销(间接调用)、需维护代理逻辑
适用场景 权限控制、远程服务、延迟初始化、缓存、资源管理、AOP
不适用场景 对象简单、无需控制、性能极度敏感

代理模式使用建议

  • 代理类应尽量轻量,避免成为性能瓶颈。
  • 可结合工厂模式或依赖注入创建代理。
  • 在 Java 中,动态代理(java.lang.reflect.Proxy)可减少静态代理的类膨胀问题。
  • Spring AOP 的 JDK Dynamic ProxyCGLIB 是代理模式的高级应用。

架构师洞见:
代理模式是“间接性”与“控制力”的完美结合。在现代架构中,其思想已渗透到微服务治理API 网关服务网格(Istio/Linkerd)AOP(面向切面编程) 的核心。例如,在服务网格中,Sidecar 代理拦截所有进出服务的流量,实现熔断、限流、加密;在 Spring 框架中,@Transactional 注解通过代理实现声明式事务管理;在前端,Proxy 对象用于实现响应式数据监听(如 Vue 3)。

未来趋势是:代理模式将与AI 安全网关结合,智能代理可动态分析请求内容并决定是否放行;在边缘计算中,设备代理将统一管理异构设备的通信协议;在元编程运行时增强中,代理将成为实现热更新、动态配置的核心机制。

掌握代理模式,有助于设计出安全、可控、可监控的系统。作为架构师,应在系统边界、服务入口、资源访问点主动引入代理层,将“控制逻辑”与“业务逻辑”分离。代理不仅是模式,更是系统治理的哲学——它告诉我们:真正的掌控力,来自于对“访问路径”的精心设计与智能干预。


网站公告

今日签到

点亮在社区的每一天
去签到