适配器模式学习

1. 定义理解

当你想使用一个已经存在的类,而它的接口不符合你的需求,或者你想创建一个可重用的类(与不兼容接口无关的类),这时候可以考虑使用适配器模式。同时它也是一种包装模式,它与装饰模式同样具有包装的功能


2. 适配器类型

  1. 类适配器 :对象继承的方式,静态的定义
  2. 对象适配器 :依赖于对象的组合(在构造方法中实例化成员对象)

3. 代码示例

3.1 类适配器

  1. 首先定义M4DataLine 代表是Micro USB,我们目的就是通过适配器能够用米4数据线连接米5手机
    class M4DataLine {
        public void connection() {
            System.out.println("使用小米4数据线连接...");
        }
    }
    
  2. 定义客户端使用的接口,与业务相关
    interface Target {
        void connection();
    }
    
    class M5DataLine implements Target {
        @Override
        public void connection() {
            System.out.println("使用小米5数据线连接...");
        }
    }
    
  3. 创建适配器类,继承了被适配类,同时实现标准接口
    class M5DataLineAdapter extends M4DataLine implements Target {
    
        @Override
        public void connection() {
            System.out.println("插入 type-c 转接头");
            super.connection();
        }
    }
    
  4. 客户端代码,测试
    public class AdapterMain {
    
        public static void main(String[] args) {
            Target target = new M5DataLine();
            target.connection();
    
            Target adapter = new M5DataLineAdapter();
            adapter.connection();
        }
    }
    
  5. 结果
    使用小米5数据线连接...
    插入 type-c 转接头
    使用小米4数据线连接...
    

3.2 对象适配器

  1. 创建适配器类,实现标准接口,将这个调用委托给实现新接口的对象来处理
    class M5DataLineAdapter implements Target {
    
        private Target target;
    
        public M5DataLineAdapter(Target target) {
            this.target = target;
        }
    
        @Override
        public void connection() {
            System.out.println("插入 type-c 转接头");
            target.connection();
        }
    }
    
    public class AdapterMain {
    
        public static void main(String[] args) {
            // 使用特殊功能类,即适配类
            Target adapter = new M5DataLineAdapter(new M5DataLine());
            adapter.connection();
        }
    }
    

4. JDK 中的适配器使用

使用适配器模式的类

java.util.Arrays#asList()
java.io.InputStreamReader(InputStream)
java.io.OutputStreamWriter(OutputStream)
  • Java I/O 库大量使用了适配器模式,如 ByteArrayInputStream 是一个适配器类,它继承了 InputStream 的接口,并且封装了一个 byte 数组。换言之,它将一个 byte 数组的接口适配成 InputStream 流处理器的接口。
  • 在 OutputStream 类型中,所有的原始流处理器都是适配器类。ByteArrayOutputStream 继承了 OutputStream 类型,同时持有一个对 byte 数组的引用。它一个 byte 数组的接口适配成 OutputString 类型的接口,因此也是一个对象形式的适配器模式的应用
  • FileOutputStream 继承了 OutputStream 类型,同时持有一个对 FileDiscriptor 对象的引用。这是一个将 FileDiscriptor 接口适配成 OutputStream 接口形式的对象型适配器模式
  • Reader 类型的原始流处理器都是适配器模式的应用。StringReader 是一个适配器类,StringReader 类继承了 Reader 类型,持有一个对 String 对象的引用。它将 String 的接口适配成 Reader 类型的接口。

5. Spring 中使用适配器模式的典型应用

  1. 在 Spring 的 AOP 里通过使用的 Advice(通知)来增强被代理类的功能。Spring 实现这一 AOP 功能的原理就使用代理模式(1、JDK 动态代理。2、CGLib 字节码生成技术代理。)对类进行方法级别的切面增强,即,生成被代理类的代理类,并在代理类的方法前,设置拦截器,通过执行拦截器中的内容增强了代理方法的功能,实现的面向切面编程。
  2. Advice(通知)的类型有:BeforeAdvice、AfterReturningAdvice、ThrowSadvice 等。每个类型 Advice(通知)都有对应的拦截器,MethodBeforeAdviceInterceptor、AfterReturningAdviceInterceptor、ThrowsAdviceInterceptor。Spring 需要将每个 Advice(通知)都封装成对应的拦截器类型,返回给容器,所以需要使用适配器模式对 Advice 进行转换

以下是源码示例:

public interface MethodBeforeAdvice extends BeforeAdvice {
    void before(Method method, Object[] args, Object target) throws Throwable;
}

public interface AdvisorAdapter {

    boolean supportsAdvice(Advice advice);

    MethodInterceptor getInterceptor(Advisor advisor);
}

class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {

    @Override
    public boolean supportsAdvice(Advice advice) {
        return (advice instanceof MethodBeforeAdvice);
    }

    @Override
    public MethodInterceptor getInterceptor(Advisor advisor) {
        MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
        return new MethodBeforeAdviceInterceptor(advice);
    }

}

默认的适配器注册表:

public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable {

    private final List<AdvisorAdapter> adapters = new ArrayList<AdvisorAdapter>(3);

    public DefaultAdvisorAdapterRegistry() {
        // 注册适配器 
        registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
        registerAdvisorAdapter(new AfterReturningAdviceAdapter());
        registerAdvisorAdapter(new ThrowsAdviceAdapter());
    }

    @Override
    public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
        if (adviceObject instanceof Advisor) {
            return (Advisor) adviceObject;
        }
        if (!(adviceObject instanceof Advice)) {
            throw new UnknownAdviceTypeException(adviceObject);
        }
        Advice advice = (Advice) adviceObject;
        if (advice instanceof MethodInterceptor) {
            // So well-known it doesn't even need an adapter.
            return new DefaultPointcutAdvisor(advice);
        }
        for (AdvisorAdapter adapter : this.adapters) {
            // 检查是否支持,这里调用了适配器的方法 
            if (adapter.supportsAdvice(advice)) {
                return new DefaultPointcutAdvisor(advice);
            }
        }
        throw new UnknownAdviceTypeException(advice);
    }

    @Override
    public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
        List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3);
        Advice advice = advisor.getAdvice();
        if (advice instanceof MethodInterceptor) {
            interceptors.add((MethodInterceptor) advice);
        }
        for (AdvisorAdapter adapter : this.adapters) {
            // 检查是否支持,这里调用了适配器的方法 
            if (adapter.supportsAdvice(advice)) {
                interceptors.add(adapter.getInterceptor(advisor));
            }
        }
        if (interceptors.isEmpty()) {
            throw new UnknownAdviceTypeException(advisor.getAdvice());
        }
        return interceptors.toArray(new MethodInterceptor[interceptors.size()]);
    }

    @Override
    public void registerAdvisorAdapter(AdvisorAdapter adapter) {
        this.adapters.add(adapter);
    }

}

6. 优缺点

  1. 可以让任何两个没有关联的类协同工作
  2. 提高了类的复用,想使用现有的类,而此类的接口标准又不符合现有系统的需要。通过适配器模式就可以让这些功能得到更好的复用。
  3. 增加了类的透明度,客户端只关注结果
  4. 使用适配器的时候,可以调用自己开发的功能,从而自然地扩展系统的功能
  5. 缺点:过多使用会导致系统凌乱,追溯困难(内部转发导致,调用A适配成B)

7. 总结

  1. 建议尽量使用对象的适配器模式,少用继承。
  2. 适配器模式属于补偿模式,专门用来在系统后期扩展、修改时使用
  3. 不可过度使用
  4. 兼有装饰模式的包装效果,又兼有代理的委托效果

转载于:https://my.oschina.net/u/3686885/blog/1835479

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值