设计模式:代理模式

文章目录

一、什么是代理模式

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

代理模式的主要优点是:

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

代理模式的主要类型有:

  • 静态代理:静态代理类在程序运行前就已经存在,一般这种代理类是由程序员来创建的。
  • 动态代理:动态代理类的源码是在程序运行时生成的,需要用到 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应用中,为了提高页面加载速度,通常会对一些频繁访问的数据进行缓存。这时,可以使用缓存代理来拦截对目标对象的请求,并检查是否已经有缓存的结果。如果有,则直接返回缓存的结果,避免重复计算或访问数据库。
    在分布式系统中,缓存代理还可以用来减少跨网络的通信次数,提高系统的响应速度。
  • 事务代理:
    在数据库操作中,为了保证数据的完整性和一致性,通常会使用事务来包裹一系列的数据库操作。事务代理可以确保这些操作要么全部成功,要么全部失败回滚,从而避免数据的不一致状态。
  • 文件访问代理:
    在文件系统中,为了控制对文件的访问权限,可以使用文件访问代理。代理会检查用户的权限,并根据权限来决定是否允许读取、写入或删除文件。

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

相关推荐
大圣数据星球2 小时前
Fluss 写入数据湖实战
大数据·设计模式·flink
思忖小下3 小时前
梳理你的思路(从OOP到架构设计)_设计模式Template Method模式
设计模式·模板方法模式·eit
思忖小下13 小时前
梳理你的思路(从OOP到架构设计)_简介设计模式
设计模式·架构·eit
liyinuo201715 小时前
嵌入式(单片机方向)面试题总结
嵌入式硬件·设计模式·面试·设计规范
aaasssdddd9617 小时前
C++的封装(十四):《设计模式》这本书
数据结构·c++·设计模式
T1an-118 小时前
设计模式之【观察者模式】
观察者模式·设计模式
思忖小下19 小时前
梳理你的思路(从OOP到架构设计)_设计模式Factory Method模式
设计模式·工厂方法模式·eit
霁月风20 小时前
设计模式——工厂方法模式
c++·设计模式·工厂方法模式
发飙的蜗牛'1 天前
23种设计模式
android·java·设计模式
NorthCastle1 天前
设计模式-创建型模式-简单工厂模式详解
设计模式·简单工厂模式