设计模式:代理模式

文章目录

一、什么是代理模式

为某一个对象提供一个代理或占位符,并由代理对象来控制对原对象的访问。

代理模式的主要优点是:

  • 功能增强:可以在不修改真实主题的情况下,增加额外的功能。
  • 控制访问:可以控制对真实主题的访问,例如,在访问真实主题之前检查权限。
  • 延迟加载:可以延迟加载真实主题,直到真正需要时才加载。

代理模式的主要类型有:

  • 静态代理:静态代理类在程序运行前就已经存在,一般这种代理类是由程序员来创建的。
  • 动态代理:动态代理类的源码是在程序运行时生成的,需要用到 JDK 的一些反射类库。

使用代理模式时,需要注意以下几点:

  • 代理模式使得代码更加灵活,但也增加了复杂性。因此,在不需要额外功能或控制访问的情况下,应避免使用代理模式。
  • 在使用动态代理时,需要注意反射操作可能带来的性能问题。

总的来说,代理模式是一种非常有用的设计模式,它可以在不修改原始对象的情况下,为其添加额外的功能或控制其访问。但是,它也可能增加代码的复杂性,因此需要谨慎使用。

二、代理模式的结构

1、介绍

  • Subject(抽象角色):通过接口或抽象类声明真实角色实现的业务方法。这是代理模式的基础,它定义了代理和真实对象都应该遵循的公共接口或抽象类。
  • Proxy(代理角色):实现抽象角色,是真实角色的代理。它持有真实角色的实例,并在调用真实角色的业务逻辑方法前后添加一些额外的操作,如日志记录、权限校验、事务管理等。代理角色通过调用真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
  • RealSubject(真实角色):实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。这是实际执行具体业务逻辑的对象。

2、代码实现样例

(1)静态代理

  • 定义一个接口及其实现类:
java 复制代码
// 抽象角色(接口)  
public interface Subject {  
    void request();  
}  
  
// 真实角色  
public class RealSubject implements Subject {  
    @Override  
    public void request() {  
        System.out.println("Called RealSubject request()");  
    }  
}  
  
// 代理角色  
public class ProxySubject implements Subject {  
    private RealSubject realSubject;  
  
    public ProxySubject(RealSubject realSubject) {  
        this.realSubject = realSubject;  
    }  
  
    @Override  
    public void request() {  
        // 在调用真实对象之前,可以添加一些操作  
        System.out.println("Before calling RealSubject request()");  
        realSubject.request();  
        // 在调用真实对象之后,可以添加一些操作  
        System.out.println("After calling RealSubject request()");  
    }  
}
  • 静态代理使用
java 复制代码
public class StaticProxyDemo {  
    public static void main(String[] args) {  
        RealSubject realSubject = new RealSubject();  
        ProxySubject proxySubject = new ProxySubject(realSubject);  
        proxySubject.request(); // 输出代理前后的额外信息和真实角色的信息  
    }  
}

(2)动态代理

动态代理通常使用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口来实现。以下是使用动态代理的示例:

java 复制代码
import java.lang.reflect.InvocationHandler;  
import java.lang.reflect.Method;  
import java.lang.reflect.Proxy;  
  
// 抽象角色(接口)  
public interface Subject {  
    void request();  
}  
  
// 真实角色  
public class RealSubject implements Subject {  
    @Override  
    public void request() {  
        System.out.println("Called RealSubject request()");  
    }  
}  
  
// 调用处理器  
public class DynamicProxyHandler implements InvocationHandler {  
    private Object subject;  
  
    public DynamicProxyHandler(Object subject) {  
        this.subject = subject;  
    }  
  
    @Override  
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
        // 在调用方法之前可以添加一些操作  
        System.out.println("Before calling method: " + method.getName());  
        Object result = method.invoke(subject, args); // 调用真实对象的方法  
        // 在调用方法之后可以添加一些操作  
        System.out.println("After calling method: " + method.getName());  
        return result;  
    }  
}  
  
// 使用动态代理  
public class DynamicProxyDemo {  
    public static void main(String[] args) {  
        RealSubject realSubject = new RealSubject();  
        InvocationHandler handler = new DynamicProxyHandler(realSubject);  
        // 创建代理对象  
        Subject proxySubject = (Subject) Proxy.newProxyInstance(  
                RealSubject.class.getClassLoader(),  
                realSubject.getClass().getInterfaces(),  
                handler  
        );  
        // 调用代理对象的方法  
        proxySubject.request(); // 输出代理前后的额外信息和真实角色的信息  
    }  
}

在这个动态代理的示例中,DynamicProxyHandler类实现了InvocationHandler接口,并重写了invoke方法。这个invoke方法会在每次调用代理对象的方法时被调用。在invoke方法中,你可以添加在调用真实对象方法前后的额外逻辑。

Proxy.newProxyInstance方法用于动态创建代理对象。它接受三个参数:类加载器、代理对象需要实现的接口数组、以及一个InvocationHandler对象。返回的是实现了指定接口的代理对象。

动态代理比静态代理更加灵活,因为它可以在运行时动态地创建代理对象,而无需为每个真实角色编写特定的代理类。但是,动态代理也有其局限性,比如它只能代理实现了接口的类。如果需要代理没有实现接口的类,则需要使用其他方法,如CGLIB等库。

三、代理模式的应用场景

代理模式的应用场景十分丰富,以下是一些具体的例子:

  • 远程代理:
    当你使用网络应用或在线服务时,客户端通常不会直接访问服务器上的对象,而是通过代理服务器进行中转。代理服务器隐藏了实际的网络通信细节,使得客户端能够更方便、安全地访问远程资源。
  • 虚拟代理:
    在图片加载应用中,对于大量或大尺寸的图片,如果一次性加载所有图片可能会导致内存不足或页面加载缓慢。这时,可以使用虚拟代理,先加载低分辨率或缩略图,当用户需要查看高清图片时,再加载真实的高清图片。
    在视频流媒体服务中,为了节省带宽和提高用户体验,通常不会一次性加载整个视频文件。而是使用虚拟代理,先加载视频的元数据或初始片段,然后按需加载后续的视频内容。
  • 安全代理:
    在企业应用中,为了保护敏感数据,如用户密码或财务数据,可以使用安全代理来限制对这些数据的直接访问。代理会检查用户的权限,并只在用户拥有足够权限时才允许访问。
    在一些金融交易系统中,安全代理可以用来监控和记录所有的交易操作,以防止未经授权的访问和恶意操作。
  • 缓存代理:
    在Web应用中,为了提高页面加载速度,通常会对一些频繁访问的数据进行缓存。这时,可以使用缓存代理来拦截对目标对象的请求,并检查是否已经有缓存的结果。如果有,则直接返回缓存的结果,避免重复计算或访问数据库。
    在分布式系统中,缓存代理还可以用来减少跨网络的通信次数,提高系统的响应速度。
  • 事务代理:
    在数据库操作中,为了保证数据的完整性和一致性,通常会使用事务来包裹一系列的数据库操作。事务代理可以确保这些操作要么全部成功,要么全部失败回滚,从而避免数据的不一致状态。
  • 文件访问代理:
    在文件系统中,为了控制对文件的访问权限,可以使用文件访问代理。代理会检查用户的权限,并根据权限来决定是否允许读取、写入或删除文件。

这些例子只是代理模式应用场景的冰山一角,实际上,代理模式的应用非常广泛,几乎可以在任何需要间接访问或控制对象访问的场景中使用。

相关推荐
晨米酱4 小时前
JavaScript 中"对象即函数"设计模式
前端·设计模式
数据智能老司机9 小时前
精通 Python 设计模式——分布式系统模式
python·设计模式·架构
数据智能老司机10 小时前
精通 Python 设计模式——并发与异步模式
python·设计模式·编程语言
数据智能老司机10 小时前
精通 Python 设计模式——测试模式
python·设计模式·架构
数据智能老司机10 小时前
精通 Python 设计模式——性能模式
python·设计模式·架构
使一颗心免于哀伤10 小时前
《设计模式之禅》笔记摘录 - 21.状态模式
笔记·设计模式
数据智能老司机1 天前
精通 Python 设计模式——创建型设计模式
python·设计模式·架构
数据智能老司机1 天前
精通 Python 设计模式——SOLID 原则
python·设计模式·架构
烛阴1 天前
【TS 设计模式完全指南】懒加载、缓存与权限控制:代理模式在 TypeScript 中的三大妙用
javascript·设计模式·typescript
李广坤1 天前
工厂模式
设计模式