首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >设计模式02——Adapter模式

设计模式02——Adapter模式

作者头像
itlemon
发布2020-04-03 18:06:19
4500
发布2020-04-03 18:06:19
举报
文章被收录于专栏:深入理解Java深入理解Java
定义

适配器设计模式,顾名思义就是将适配器的作用总结抽象成为一种代码的组织方式,将现有的代码通过适配器进行适配,以满足项目对另外一个类或者接口的要求。换句话说就是将一个类的接口适配(包装/转换)成客户(调用者)希望的另一个接口。适配器设计模式有以下两种形式:

  • 类适配器模式(使用继承的适配器)
  • 对象适配器模式(使用委托的适配器)
问题引入

我们常用的笔记本电脑的配件中就有一个适配器,负责将220V交流电转换成为12V的直流电给笔记本电脑供电,它存在的作用就是将220V交流电转换成为12V的直流电。所以它就是适配器,220V交流电就是被适配的对象,而12V直流电就是转换后的目标对象,笔记本电脑就是这个目标对象的调用者。

适配器设计模式在JDK源码中的应用

学习适配器设计模式,当然也需要从JDK中去寻找它的踪迹,在JDK源码中,采用适配器设计模式的地方很多,比如最常见的IO转换流和集合等。接下来我们一起从源码中来分析适配器设计模式是如何使用起来的。 我们一起阅读一下java.io.InputStreamReader(InputStream)的部分源码:

InputStreamReader的作用是将字节流转换为字符流,是它们之间转换的桥梁(适配器),也就是说,InputStreamReader就是适配器,负责将InputStream转换为Reader,这样就可以使用Reader的方法来执行各项操作。

package java.io;

import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import sun.nio.cs.StreamDecoder;

public class InputStreamReader extends Reader {

    private final StreamDecoder sd;
    
    public InputStreamReader(InputStream in) {
        super(in);
        try {
            sd = StreamDecoder.forInputStreamReader(in, this, (String)null); // ## check lock object
        } catch (UnsupportedEncodingException e) {
            throw new Error(e);
        }
    }
    
    public String getEncoding() {
        return sd.getEncoding();
    }

    public int read() throws IOException {
        return sd.read();
    }

    public int read(char cbuf[], int offset, int length) throws IOException {
        return sd.read(cbuf, offset, length);
    }

    public boolean ready() throws IOException {
        return sd.ready();
    }

    public void close() throws IOException {
        sd.close();
    }
}

上面的代码是经过删减后的部分代码,删除了源码中的注释以及几个构造方法,从阅读源码来看,这个适配器设计模式的形式采用的是“对象适配器模式”,至于何是“对象适配器模式”,我们将在后面的学习中介绍,读者可以暂时不必理会何是“对象适配器模式”。

手动实现适配器设计模式

接下来,我们将手动实现两种适配器设计模式,用简单的代码来说明适配器设计模式是如何运转起来的。

以前小时候都玩过QQ游戏,有时候需要充值Q币,使用Q币在游戏中购买道具,这种情景就可以完全适用适配器设计模式,那么在这种情景中,使用Q币来购买游戏道具是我们需求,也就是我们的目标(Target),而现在的现状是我们有人民币,那么人民币就是被适配的对象(Adaptee),由于人民币不能直接在游戏中购买道具,它需要被转换成Q币才可以进行交易,所以我们还需要一个适配器(Adapter),负责将人民币转换成Q币。 根据以上的文字,我们可以将其总结成为一个表格,可以很方便地理清关系:

角色

扮演者

作用

Target

购买游戏道具的接口

有充值Q币接口,有使用Q币充值游戏道具的接口

Adapter

Q币充值器

将人民币转换成为Q币,并完成充值

Adaptee

人民币

被适配,被转换的对象

根据以上的关系,我们分别来创建各个角色对应的类或者接口。这里我们模拟了使用人民币充值Q币,使用Q币购买游戏道具的案例,假设一个单位的人民币可以充值10Q币,每个Q币可以一个游戏道具。

示例代码1:类适配器设计模式(使用继承的适配器)
  • Target

我们的目标是有一个接口,这个接口可以购买游戏道具,但是需要使用Q币来进行购买。在适配器设计模式里,它是我们需要最终的目标。

package cn.itlemon.design.pattern.chapter02.adapter.example3;

/**
 * @author jiangpingping
 * @date 2018/9/6 下午7:43
 */
public interface TargetInterface {

    /**
     * 购买qCoinCount个游戏道具
     */
    void buyGameProps();

}
  • Adaptee

现在的现状是手头上有人民币,所以需要有一个Q币充值器,将人民币换成相同价值的Q币。在适配器设计模式里面,人民币就是需要被适配的对象,因为它不能直接用来购买游戏道具,但是得必须通过它才可以充值Q币,才能满足要求。

package cn.itlemon.design.pattern.chapter02.adapter.example3;

/**
 * 人民币
 *
 * @author jiangpingping
 * @date 2018/9/6 下午7:45
 */
public class Rmb {

    private int count;

    public Rmb(int count) {
        this.count = count;
    }

    public int getCount() {
        return this.count;
    }

}
  • Adapter

所以我们需要一个Q币充值器,将人民币换成Q币,然后还具备购买游戏道具的功能。在适配器设计模式中,Q币充值器就是我们所需的适配器。

package cn.itlemon.design.pattern.chapter02.adapter.example3;

/**
 * Q币充值器
 *
 * @author jiangpingping
 * @date 2018/9/6 下午7:31
 */
public class QCoinRechargeableDevice extends Rmb implements TargetInterface {

    private int qCoinCount;

    public QCoinRechargeableDevice(int rmbCount) {
        super(rmbCount);
        this.qCoinCount = getCount() * 10;
    }

    @Override
    public void buyGameProps() {
        System.out.println("一共购买了" + qCoinCount + "个道具");
    }
}
  • Main

这里写一个Main方法,来验证我们上面设计的适配器设计模式,主要代码如下:

package cn.itlemon.design.pattern.chapter02.adapter.example3;

/**
 * @author jiangpingping
 * @date 2018/9/6 下午9:26
 */
public class Main {

    public static void main(String[] args) {
        TargetInterface qCoinRechargeableDevice = new QCoinRechargeableDevice(10);
        qCoinRechargeableDevice.buyGameProps();
    }
}

这里,我们向Q币充值器充值10个单位的人民币,就可以完成购买100个游戏道具转变。本来人民币不能直接用来购买游戏道具,使用适配器设计模式之后,就可以完成我们购买游戏道具的需求。

使用继承的适配器UML类图
这里写图片描述
这里写图片描述

使用继承的适配器有一个特点就是Adapter继承了Adaptee,并实现了Target,这就是三者之间的关系。

示例代码2:对象适配器设计模式(使用委托的适配器)

这里仅仅是贴出代码,对于各个类的说明,在上面都已经进行了阐述。

  • Target
package cn.itlemon.design.pattern.chapter02.adapter.example4;

/**
 * @author jiangpingping
 * @date 2018/9/10 下午9:16
 */
public abstract class TargetInterface {

    /**
     * 购买qCoinCount个游戏道具
     */
    public abstract void buyGameProps();
}
  • Adaptee
package cn.itlemon.design.pattern.chapter02.adapter.example4;

/**
 * 人民币
 *
 * @author jiangpingping
 * @date 2018/9/6 下午7:45
 */
public class Rmb {

    private int count;

    public Rmb(int count) {
        this.count = count;
    }

    public int getCount() {
        return this.count;
    }

}
  • Adapter
package cn.itlemon.design.pattern.chapter02.adapter.example4;

/**
 * @author jiangpingping
 * @date 2018/9/10 下午9:18
 */
public class QCoinRechargeableDevice extends TargetInterface {

    private Rmb rmb;

    public QCoinRechargeableDevice(Rmb rmb) {
        this.rmb = rmb;
    }

    @Override
    public void buyGameProps() {
        System.out.println("一共购买了" + rmb.getCount() * 10 + "个道具");
    }
}
  • Main
package cn.itlemon.design.pattern.chapter02.adapter.example4;

/**
 * @author jiangpingping
 * @date 2018/9/10 下午9:16
 */
public class Main {

    public static void main(String[] args) {
        TargetInterface qCoinRechargeableDevice = new QCoinRechargeableDevice(new Rmb(10));
        qCoinRechargeableDevice.buyGameProps();
    }
}
使用委托(对象)的适配器UML类图
这里写图片描述
这里写图片描述

使用委托的适配器有一个特点就是Adapter拥有了Adaptee,并继承了Target抽象类,这就是三者之间的关系。

浅析适配器模式中的重要角色

适配器设计模式也是一个比较常用的设计模式之一,现对适配器设计模式中的角色进行浅析。

  • Target(对象) 该角色负责定义最终的需求,也就是使用适配器模式之后的最终效果。在本次示例中,TargetInterface就是扮演了这个Target角色。
  • Adaptee(被适配) 该角色定义的是原始的功能,它也许无法直接被利用,但是又不能随意更改,所以它就需要被适配,使得在不修改原始代码的情况下能激活Target的功能。在本次示例中,Rmb扮演了这个角色。
  • Adapter(适配) 该角色是适配器设计模式的核心角色,他负责适配AdapteeTarget,使得Adaptee来满足Target的需求。在本次示例中,QCoinRechargeableDevice扮演了这个角色。
  • Client(请求者) 该角色负责调用Target的方法来进行一系列的逻辑处理。在本次示例中,Main类扮演了这个角色。
适配器设计模式UML类图

分析完适配器设计模式的重要角色,当然也得理清适配器设计模式的UML类图。

  • 使用继承的适配器设计模式类图
这里写图片描述
这里写图片描述
  • 使用委托的适配器设计模式类图
这里写图片描述
这里写图片描述
为什么要使用适配器设计模式

我们往往有这种思想,要使用什么类的方法,直接使用不就OK了,或者稍微修改一下已有的代码不就可以使用了吗?其实这种思想是不正确的,因为在现有类的基础下,很多类的方法都经过了严格的测试,贸然地去修改他容易造成意外情况的发生,我们使用适配器设计模式,往往无需修改现有的代码,直接在现有的代码的基础上创建新的代码,这样即使出了错误,我们也能很快从我们新写的代码中找出端倪。使用适配器设计模式,也是对现有代码的一种重复利用。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-09-10 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 定义
  • 问题引入
  • 适配器设计模式在JDK源码中的应用
  • 手动实现适配器设计模式
    • 示例代码1:类适配器设计模式(使用继承的适配器)
      • 使用继承的适配器UML类图
        • 示例代码2:对象适配器设计模式(使用委托的适配器)
          • 使用委托(对象)的适配器UML类图
          • 浅析适配器模式中的重要角色
          • 适配器设计模式UML类图
          • 为什么要使用适配器设计模式
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档