Java设计模式-适配器模式

1. 首先对适配器模式做下简单介绍:
适配器模式,从名字上大概可以了解是做适配处理的。是做什么样的适配呢?就是将一个类的接口转换成想要的接口,以便实现不同的接口。这听不起来感觉不太容易实现,但有了设计模式,实现起来还是挺容易的。首先回忆一下装饰者模式(它与适配器模式有点类似,但意图是不相同的):我们将对象包装起来,赋予它们新的职责。而现在则是以不同目的,包装某些对象:让他们的接口看起来不像自己而是别的东西。
以上的描述听起来还是有点抽象了,举个现实中的例子,比如说:你买了一个港版的苹果手机,回来充电的时候可能还需要购买一个英标转换头(这算是一个交流电适配器)。那么它的作用就很容易理解了:它位于英式插头和中式插座的中间,它的工作就是将中式插座转换成英式插座,好让英式插头可以插进这个插座能够正常给手机充电。
好了,这是真实世界的适配器,那面向对象适配器又是什么?其实,OO适配器和真实世界的适配器扮演者同样的角色:将一个接口转换成另一个接口,以符合某种业务场景。

2. 接着,我们使用代码实现一下以上例子(有两种实现方式:对象适配器和类适配器模式),我们先通过对象适配器方式(常用)来实现:

  • 定义英标插座接口
/**
 * @className: BritishSocket
 * @summary: 英标插座
 * @Description: TODO
 * @author: helon
 * date: 2018/11/24 11:36 PM
 * version: v1.0
 */
public interface BritishSocket {
    /**
     * @Summary 英标插座
     * @Description 港版iPhone充电插头是英标的,需要英标插座才能满足充电
     * @Author helon
     * @Date 2018/11/24 11:37 PM
     * @Param []
     * @return void
     **/
    void britishSocketSupport();
}
  • 定义一个港版的iPhoneX类,只能使用英标插座充电
/**
 * @className: IPhoneX
 * @summary: 港版iPhoneX
 * @Description: TODO
 * @author: helon
 * date: 2018/11/24 11:40 PM
 * version: v1.0
 */
public class IPhoneX {
    /**
     * @Summary iPhone手机充电
     * @Description 必须使用英标插座充电
     * @Author helon
     * @Date 2018/11/24 11:43 PM
     * @Param [britishSocket]
     * @return void
     **/
    public void charging(BritishSocket britishSocket) {
        britishSocket.britishSocketSupport();
        System.out.println("正在使用英标插座充电...");
    }
}
  • 定义我们日常使用的插座类(中国标准插座):
/**
 * @Summary 中国标准插座
 * @Description 提供充电方法
 * @Author helon
 * @Date 2018/11/24 11:27 PM
 * @Param 
 * @return  
 **/
public class ChineseSocket {
    /**
     * @Summary 中国标准插座
     * @Description
     * @Author helon
     * @Date 2018/11/24 11:30 PM
     * @Param []
     * @return void
     **/
    public void chineseSocketSupport() {
        System.out.println("提供中国标准插座");
    }
}
  • 接下来就是关键的步骤,需要我们实现一个适配器,能够将我们日常的插座通过这个适配器转换成英标插座,来满足给手机充电
/**
 * @className: ChineseSocketAdapter
 * @summary: 插座适配器(对象适配器模式)
 * @Description: 通过适配器来使中国标准插座转换为英标插座,来满足港版iPhone充电
 * @author: helon
 * date: 2018/11/24 11:44 PM
 * version: v1.0
 */
public class ChineseSocketAdapter implements BritishSocket {
    private ChineseSocket chineseSocket;
    public ChineseSocketAdapter(ChineseSocket chineseSocket) {
        this.chineseSocket = chineseSocket;
    }
    @Override
    public void britishSocketSupport() {
        chineseSocket.chineseSocketSupport();
        System.out.println("通过中国标准插座来转换适配成英标插座");
    }
}
  • 最后,我们测试运行看一下结果:
/**
 * @className: TestMain
 * @summary: 测试适配器模式
 * @Description: TODO
 * @author: helon
 * date: 2018/11/24 11:56 PM
 * version: v1.0
 */
public class TestMain {
    public static void main(String[] args) {
        System.out.println("====对象适配器====");
        IPhoneX iPhoneX = new IPhoneX();
        //声明一个适配器,该适配器提供了英标插座,中国标准的插头(能够接入中国标准插座)
        ChineseSocketAdapter chineseSocketAdapter = new ChineseSocketAdapter(new ChineseSocket());
        //开始充电
        iPhoneX.charging(chineseSocketAdapter);
    }
}

//执行结果
====对象适配器====
提供中国标准插座
通过中国标准插座来转换适配成英标插座
正在使用英标插座充电...
  • 以上就是具体实现的代码,我们再看一下关系类图:
    对象适配器模式类图

  • 使用类适配器模式实现案例,修改Adapter类:

/**
 * @className: ChineseSocketClassAdapter
 * @summary: 类适配器模式
 * @Description: 通过实现英标接口,并继承ChineseSocket类的方式实现
 * 优点:实现简单
 * 缺点:由于Java单继承的缘故,目标类必须是接口(BritishSocket),以便于适配器去继承源类(ChineseSocket)
 * 这样就导致了适配器里暴露了源类的方法,使用起来的成本就增加了
 * @author: helon
 * date: 2018/11/25 5:08 PM
 * version: v1.0
 */
public class ChineseSocketClassAdapter extends ChineseSocket implements BritishSocket{
    @Override
    public void britishSocketSupport() {
        chineseSocketSupport();
        System.out.println("类适配器方式实现英标转换");
    }
}

由于Java单继承的缘故,目标类必须是接口(BritishSocket),以便于适配器去继承源类(ChineseSocket),这样就导致了适配器里暴露了源类的方法,使用起来的成本就增加了,所以推荐使用对象适配器方式。
类对象适配器的类图如下:
类适配器类图

3. JDK中的适配器模式使用场景:
其实在jdk的InputStreamReader和OutputStreamWriter都使用到了适配器模式,就拿InputStreamReader来说吧,首先它集成了Reader类,构造方法中需要传入InputStream类对象,这样做的目的就是需要将InputStream对象适配到Reader。其中,InputStreamReader就是适配器,Reader是目标对象,而InputStream是源对象。同理,OutputStreamWriter也是一个适配器类,它是将OutputStream适配到Writer。

4. 适配器模式和装饰者模式的差异:
说了这么多,貌似有好多地方都和装饰者模式很相似,容易混淆。那么它们两者具体的差异有哪些呢,或者说怎么才能不把它们弄混淆?其实,如果使用了装饰者模式,那么一定表示有新的行为或功能要加入到设计中,说白了装饰者模式就是对源组件进行功能增强,不会做适配或转换的意图。那么,适配器模式在不改变源代码的情况下,做到对目标功能的适配,一句话概括就是:将一个接口转换成另一个接口,以符合客户端使用的期望,但装饰者模式不会做转换的工作。其实,适配器模式有好多的实现方式,但只要是基于适配器模式核心思想,模式变得再多也会很容易理解。

5. 总结
在工作中,也会经常遇到需要使用适配器模式来解决的场景,比如:当要接入一个新的支付渠道的时候,前提是线上已经有一家正常运行的支付渠道。需求是将部分银行修改为新接入的这个厂商提供的接口,但发现新厂商的接口和原使用的接口有一些字段差异,那么我们需要考虑如何用最小的改动,最小的风险,来做这次升级改造。这个时候就可以考虑使用适配器模式,将新接口适配到我们原来的接口,将对外业务调用的接口保持不变,只需要在适配器中做下转换操作就可以了,这样外部业务接口可以做到零改动,降低bug的风险。
好了,以上这些就是我对适配器模式的理解和总结,如有什么疑义,欢迎指正,一起交流学习。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值