自学内容网 自学内容网

JAVA代理模式和适配器模式

Java 代理模式和适配器模式

代理模式(Proxy Pattern)

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

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

适配器模式(Adapter Pattern)

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

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

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

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

代理模式的使用举例

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

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

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

目标接口:

public interface Service {
    void doTask();
}

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

静态代理类:

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: 任务执行完成");
    }
}

测试代码:

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

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

  • 输出
LoggingProxy: 开始执行任务...
RealService: 执行任务
LoggingProxy: 任务执行完成

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

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

目标接口和实现:

public interface Service {
    void doTask();
}

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

动态代理处理类:

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;
    }
}

测试代码:

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(); // 使用动态代理调用方法
    }
}

  • 输出
LoggingHandler: 方法 doTask 开始执行
RealService: 执行任务
LoggingHandler: 方法 doTask 执行完成

适配器模式使用举例

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

示例:适配老版本接口

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

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

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

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

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

  • 输出
OldClass: 调用旧方法

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

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

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

使用代理模式的时机

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

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

使用适配器模式时机

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

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

总结

模式适用场景优点缺点
代理模式功能增强(日志、权限、远程调用等)灵活添加功能,解耦目标对象和功能实现静态代理增加代码量,动态代理可能增加复杂度
适配器模式接口不兼容(老接口适配新接口、第三方库整合等)解决接口兼容问题,增强代码复用性若接口变化频繁,适配器可能需要频繁修改

原文地址:https://blog.csdn.net/yang2330648064/article/details/144778265

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!