JAVA代理模式和适配器模式

文章目录

Java 代理模式和适配器模式

代理模式(Proxy Pattern)

  • 代理模式是一种结构型设计模式,它为对象提供一个代理对象,以控制对目标对象的访问。代理模式可以在不改变目标对象的前提下,向目标对象添加额外的功能(如权限控制、日志记录、懒加载等)。

  • 特点
  1. 代理对象充当目标对象的替代品。
  2. 代理对象可以对目标对象的访问进行控制或增强。
  3. 常见场景:访问控制、性能优化(如懒加载)、日志记录、分布式调用(如 RPC)等。

适配器模式(Adapter Pattern)

  • 适配器模式也是一种结构型设计模式,它用于解决两个接口不兼容的问题。适配器模式通过引入一个适配器类,使得一个类的接口能够兼容另一个类的接口,最终实现它们可以协同工作。

  • 特点
  1. 适配器充当"中间翻译者",解决接口不匹配的问题。
  2. 常见场景:兼容老系统(向后兼容)、集成第三方库,或者对接口的统一抽象。

代理模式和适配器模式的区别

对比点 代理模式 适配器模式
主要目标 控制对目标对象的访问或增加功能 解决两个接口不兼容的问题
角色 代理对象、目标对象 目标对象、适配器、需要适配的接口
使用场景 访问控制、权限校验、日志记录、性能优化、分布式调用等 将现有的类或接口适配为目标接口,解决接口不兼容的问题
实现方式 通过代理类(静态代理或动态代理)实现对目标对象方法的增强 通过适配器类实现接口的转换
是否增强功能 代理模式可以对目标对象的功能进行增强 适配器不增强功能,只是转换接口

代理模式的使用举例

Java 中代理模式可以分为两种实现方式:

  1. 静态代理:代理类由手动编写,在编译时确定。
  2. 动态代理 :代理类在运行时动态生成,常用的是 Java 自带的动态代理(基于 java.lang.reflect.Proxy)和 CGLIB 动态代理。

静态代理实现:用代理模式记录方法调用日志

目标接口:

java 复制代码
public interface Service {
    void doTask();
}

public class RealService implements Service {
    @Override
    public void doTask() {
        System.out.println("RealService: 执行任务");
    }
}

静态代理类:

java 复制代码
public class LoggingProxy implements Service {
    private final Service realService;

    public LoggingProxy(Service realService) {
        this.realService = realService;
    }

    @Override
    public void doTask() {
        System.out.println("LoggingProxy: 开始执行任务...");
        realService.doTask();
        System.out.println("LoggingProxy: 任务执行完成");
    }
}

测试代码:

java 复制代码
public class StaticProxyExample {
    public static void main(String[] args) {
        Service realService = new RealService();
        Service proxy = new LoggingProxy(realService);

        proxy.doTask(); // 使用代理对象调用方法
    }
}

  • 输出
bash 复制代码
LoggingProxy: 开始执行任务...
RealService: 执行任务
LoggingProxy: 任务执行完成

动态代理实现:使用 Java 动态代理记录方法调用日志

  • 动态代理更加灵活,适合在运行时为任意接口生成代理。

目标接口和实现:

java 复制代码
public interface Service {
    void doTask();
}

public class RealService implements Service {
    @Override
    public void doTask() {
        System.out.println("RealService: 执行任务");
    }
}

动态代理处理类:

java 复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class LoggingHandler implements InvocationHandler {
    private final Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("LoggingHandler: 方法 " + method.getName() + " 开始执行");
        Object result = method.invoke(target, args);
        System.out.println("LoggingHandler: 方法 " + method.getName() + " 执行完成");
        return result;
    }
}

测试代码:

java 复制代码
public class DynamicProxyExample {
    public static void main(String[] args) {
        Service realService = new RealService();

        // 创建动态代理
        Service proxy = (Service) Proxy.newProxyInstance(
                realService.getClass().getClassLoader(),
                realService.getClass().getInterfaces(),
                new LoggingHandler(realService)
        );

        proxy.doTask(); // 使用动态代理调用方法
    }
}

  • 输出
bash 复制代码
LoggingHandler: 方法 doTask 开始执行
RealService: 执行任务
LoggingHandler: 方法 doTask 执行完成

适配器模式使用举例

  • 适配器模式通常用于解决接口不兼容的问题,例如当我们需要将一个老接口适配为新的接口,或者需要集成第三方库时。

示例:适配老版本接口

  • 定义目标接口和老版本接口:
java 复制代码
// 新接口
public interface NewInterface {
    void newMethod();
}

// 老接口
public class OldClass {
    public void oldMethod() {
        System.out.println("OldClass: 调用旧方法");
    }
}
  • 适配器类:
java 复制代码
public class Adapter implements NewInterface {
    private final OldClass oldClass;

    public Adapter(OldClass oldClass) {
        this.oldClass = oldClass;
    }

    @Override
    public void newMethod() {
        // 在新方法中调用旧方法
        oldClass.oldMethod();
    }
}
  • 测试代码:
java 复制代码
public class AdapterExample {
    public static void main(String[] args) {
        OldClass oldClass = new OldClass();
        NewInterface adapter = new Adapter(oldClass);

        adapter.newMethod(); // 调用新接口的方法,但实际调用的是旧接口的方法
    }
}

  • 输出
bash 复制代码
OldClass: 调用旧方法

适配器模式扩展:对象适配 vs 类适配

  • 对象适配器 :通过组合(如上述示例中,适配器持有一个 OldClass 对象)。
  • 类适配器 :通过继承来实现适配,适配器继承旧类并实现目标接口(要求旧类不能是 final,且适配器只能适配一个类)。

代理与适配器模式使用建议

使用代理模式的时机

  • 场景 1:需要为对象添加额外功能,而又不想直接修改目标对象的代码(如访问控制、日志记录等)。
  • 场景 2:需要延迟加载目标对象,或者在访问目标对象时添加缓存机制。

  • 推荐使用动态代理
    • 如果需要代理多个类或接口,动态代理更灵活且代码量更少。
    • 如果只针对一个类或接口,可以直接使用静态代理。

使用适配器模式时机

  • 场景 1:需要整合现有的旧系统或第三方库,而它们的接口与当前系统的接口不兼容。
  • 场景 2:需要对不同类的接口进行统一,提供一个一致的调用方法。

  • 建议:
    • 如果需要适配的类较多,可以考虑通过继承或组合方式实现适配器。
    • 如果适配的接口或类较复杂,可以结合工厂模式,统一管理适配器的实例化。

总结

模式 适用场景 优点 缺点
代理模式 功能增强(日志、权限、远程调用等) 灵活添加功能,解耦目标对象和功能实现 静态代理增加代码量,动态代理可能增加复杂度
适配器模式 接口不兼容(老接口适配新接口、第三方库整合等) 解决接口兼容问题,增强代码复用性 若接口变化频繁,适配器可能需要频繁修改
相关推荐
辞半夏丶北笙1 小时前
最近最少使用算法(LRU最近最少使用)缓存替换算法
java·算法·缓存
星如雨グッ!(๑•̀ㅂ•́)و✧1 小时前
Java NIO全面详解
java·python·nio
taopi20243 小时前
android java系统弹窗的基础模板
android·java·开发语言
松仔log3 小时前
Java多线程——对象的组合
java·开发语言·jvm
酷爱码3 小时前
springboot 动态配置定时任务
java·spring boot·后端
从未止步..4 小时前
Jenkins未在第一次登录后设置用户名,第二次登录不进去怎么办?
java·运维·jenkins
老马啸西风4 小时前
IM 即时通讯系统-42-基于netty实现的IM服务端,提供客户端jar包,可集成自己的登录系统
java
2501_903238654 小时前
Java 9模块开发:Eclipse实战指南
java·开发语言·eclipse·个人开发
test猿4 小时前
hive为什么建表,表存储什么
java
程序猿零零漆5 小时前
SpringCloud系列教程:微服务的未来(二十)Seata快速入门、部署TC服务、微服务集成Seata
java·spring·spring cloud·微服务