二十三种设计模式-代理模式

一、定义与核心思想

代理模式是一种结构型设计模式,其核心思想是提供一个代理对象,用以控制对实际对象的访问。通过代理对象,可以在不改变实际对象的情况下,添加各种功能,如权限控制、懒加载、缓存、远程调用等。

二、组成要素

代理模式主要由以下几个要素组成:

  1. 抽象主题(Subject)

    • 这是一个接口或抽象类,定义了代理对象和实际对象共同的接口,使得代理对象可以在客户端中替代实际对象使用。

    • 例如,定义一个Subject接口,其中声明了request()方法。

  2. 实际主题(RealSubject)

    • 实现抽象主题接口的具体类,定义了代理所代表的真实对象。实际主题对象实现了业务逻辑,是客户端最终需要调用的对象。

    • 例如,RealSubject类实现了Subject接口,在request()方法中实现了具体的业务逻辑。

  3. 代理(Proxy)

    • 也实现了抽象主题接口,内部包含一个对实际主题对象的引用。代理对象在调用实际主题对象的方法前后可以添加额外的逻辑,从而控制对实际主题对象的访问。

    • 例如,Proxy类实现了Subject接口,并且有一个RealSubject类型的成员变量。在request()方法中,代理对象可以进行权限检查、日志记录等操作,然后调用实际主题对象的request()方法。

三、实现示例

以下是使用Java语言实现代理模式的一个简单示例:

复制代码
// 抽象主题接口
interface Subject {
    void request();
}

// 实际主题类
class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("实际对象的请求");
    }
}

// 代理类
class Proxy implements Subject {
    private RealSubject realSubject;

    @Override
    public void request() {
        preRequest();
        if (realSubject == null) {
            realSubject = new RealSubject();
        }
        realSubject.request();
        postRequest();
    }

    private void preRequest() {
        System.out.println("代理对象的预处理操作,如权限检查");
    }

    private void postRequest() {
        System.out.println("代理对象的后处理操作,如日志记录");
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        Subject proxy = new Proxy(); // 直接创建代理对象
        proxy.request(); // 通过代理对象调用方法
    }
}

在这个示例中,客户端通过代理对象proxy调用request()方法。代理对象在调用实际主题对象RealSubjectrequest()方法前后,分别进行了预处理和后处理操作,如权限检查和日志记录。

四、优点

  1. 职责分离

    • 代理对象可以将客户端与实际主题对象解耦,使得客户端不需要直接与实际主题对象交互,从而降低了系统的耦合度。
  2. 功能扩展

    • 代理对象可以在不修改实际主题对象的情况下,添加各种功能,如权限控制、缓存、懒加载等,符合开闭原则。
  3. 控制访问

    • 代理对象可以控制对实际主题对象的访问,例如,根据用户的权限决定是否允许调用实际主题对象的方法。
  4. 远程调用

    • 代理对象可以处理远程方法调用的细节,如网络通信、序列化/反序列化等,使得客户端可以透明地调用远程对象的方法。
  5. 延迟加载

    • 代理对象可以实现懒加载,只有在第一次调用方法时才创建实际主题对象,从而提高系统的性能。

五、缺点

  1. 增加复杂性

    • 代理模式会增加系统的复杂性,因为需要创建额外的代理类和管理代理对象与实际对象之间的关系。
  2. 性能开销

    • 代理对象的调用会增加额外的性能开销,特别是当代理对象的逻辑比较复杂时,可能会对系统的性能产生一定的影响。

六、应用场景

  1. 权限控制

    • 通过代理对象可以控制对实际对象的访问权限,例如,根据用户的权限决定是否允许调用某个方法或访问某个资源。

    • 例如,实现一个权限控制代理,只有具备相应权限的用户才能通过代理对象调用实际对象的方法。

  2. 远程调用

    • 代理对象可以处理远程方法调用的细节,使得客户端可以透明地调用远程服务器上的方法。

    • 例如,使用RMI(Remote Method Invocation)技术实现远程对象调用,客户端通过代理对象调用远程服务器上的方法。

  3. 懒加载

    • 代理对象可以实现懒加载,只有在第一次调用方法时才创建实际对象,从而提高系统的性能。

    • 例如,加载一个大型的数据库连接对象时,可以使用懒加载代理来延迟创建连接对象,直到真正需要使用时才创建。

  4. 缓存

    • 代理对象可以缓存方法的调用结果,当再次调用相同的方法时,直接返回缓存的结果,从而提高系统的性能。

    • 例如,缓存网页的静态内容,当用户再次请求相同的内容时,直接从缓存中获取,而不需要重新生成。

  5. 日志记录

    • 代理对象可以在调用实际对象的方法前后记录日志,用于监控和调试。

    • 例如,记录方法的调用时间、参数、返回值等信息,帮助开发者进行问题定位和性能优化。

七、动态代理

除了静态代理,Java还支持动态代理。动态代理可以在运行时动态创建代理对象,而不需要在编译时定义代理类。动态代理主要使用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口来实现。

动态代理示例
复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 抽象主题接口
interface Subject {
    void request();
}

// 实际主题类
class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("实际对象的请求");
    }
}

// 调用处理器
class MyInvocationHandler implements InvocationHandler {
    private Object target;

    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        preRequest();
        Object result = method.invoke(target, args);
        postRequest();
        return result;
    }

    private void preRequest() {
        System.out.println("代理对象的预处理操作,如权限检查");
    }

    private void postRequest() {
        System.out.println("代理对象的后处理操作,如日志记录");
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        Subject realSubject = new RealSubject(); // 创建实际主题对象
        MyInvocationHandler handler = new MyInvocationHandler(realSubject); // 创建调用处理器并传入实际主题对象

        Subject proxy = (Subject) Proxy.newProxyInstance(
            Subject.class.getClassLoader(), // 目标类加载器
            new Class<?>[]{Subject.class}, // 目标类实现的接口
            handler // 调用处理器
        );

        proxy.request(); // 通过代理对象调用方法
    }
}

在这个示例中,客户端通过Proxy.newProxyInstance方法动态创建了一个代理对象proxy。代理对象的调用处理器MyInvocationHandler在调用实际主题对象RealSubject的方法前后,分别进行了预处理和后处理操作。

总结

代理模式通过代理对象控制对实际对象的访问,可以在不修改实际对象的情况下,添加各种功能,如权限控制、懒加载、缓存、远程调用等。代理模式在实际开发中非常有用,选择静态代理还是动态代理取决于具体的需求和场景。

相关推荐
小屁猪qAq1 小时前
设计模式总纲
开发语言·c++·设计模式
小简GoGo2 小时前
前端常用设计模式快速入门
javascript·设计模式
会员果汁5 小时前
17.设计模式-单例模式(Singleton)
单例模式·设计模式
派大鑫wink6 小时前
【Day37】MVC 设计模式:原理与手动实现简易 MVC 框架
java·设计模式·mvc
会员果汁6 小时前
18.设计模式-桥接模式
设计模式·桥接模式
老蒋每日coding6 小时前
AI Agent 设计模式系列(九)——学习和适应模式
人工智能·学习·设计模式
da_vinci_x20 小时前
武器设计实战:一把大剑裂变 5 种属性?Structure Ref 的“换肤”魔法
游戏·3d·设计模式·ai作画·aigc·设计师·游戏美术
刀法孜然1 天前
23种设计模式 3 行为型模式 之3.7 command 命令模式
设计模式·命令模式
一条闲鱼_mytube1 天前
智能体设计模式(二)反思-工具使用-规划
网络·人工智能·设计模式
老蒋每日coding1 天前
AI智能体设计模式系列(七)—— 多 Agent 协作模式
设计模式