当前位置: 首页 > news >正文

一文学会如何使用适配器模式

适配器模式又叫做变压器模式,它的功能是将一个类的接口变成客户端所期望的另一种接口,从而使原本因接口不匹配导致无法在一起工作的两个类能一起工作,属于结构型设计模式。

也就是说当前系统存在两种接口A和B,客户只支持访问A接口,但是当前系统没有A接口对象,但是有B接口对象,但客户无法识别B接口,因此需要通过一个适配器C,将B接口内容转换成A接口,从而使得客户能够从A接口获取到B接口内容。

在软件开发中,基本上任何问题都可以通过增加一个中间层进行解决。适配器模式其实就是一个中间层。综上,适配器模式其实起着转化/委托的作用,将一种接口转化为另一种符合需求的接口。

应用场景

提供一个转换器,将当前系统存在的一个对象转化为客户端能够访问的接口对象。适配器适用于以下几种业务场景:

  • 已经存在的类,它的方法和需求不匹配的情况
  • 适配器模式不是软件设计阶段考虑的设计模式,是随着软件维护,由于不同产品、不同厂家造成功能类似而接口不同情况下的解决方案。

适配器模式一般包含三种角色:

  • 目标角色(Target):期望的接口
  • 源角色(Adaptee):存在于系统中,内容满足客户需求(需转换),但接口不匹配的实例
  • 适配器(Adapter):将原角色转换为目标角色的实例

适配器模式各角色之间的关系如:

假设当前系统中,客户端需要访问的是Target接口,但Target接口没有一个实例符合需求,而源角色实例符合需求;但是客户端无法直接使用源角色;因此我们需要一个适配器来进行中转,让源角色转换为目标角色的接口形式。

适配器模式有三种形式:类适配器、对象适配器、接口适配器。

类适配器

类适配器的原理就是通过继承来实现适配器功能。

具体做法:让Adapter实现Target接口,并且继承Adaptee,这样Adapter就具备两者的特性,就可以将两者进行转化。下面来看类图:

在这里插入图片描述

在我们的日常生活中,民用电基本都是220V,我们使用电器时通常都需要电源适配器进行转换。下面我们就来模拟这个场景:

首先创建Adaptee角色,表示需要被转换的对象220V电流:

public class Ac220 {

    public int outPut220V() {
        int output = 220;
        System.out.println("电压220V");
        return output;
    }

}

创建Target角色,表示5v直流电:

public interface Dc5 {

    int outputDc5();

}

创建Adapter角色适配器代表电源转换头:

public class PowerAdapter extends Ac220 implements Dc5{

    @Override
    public int outputDc5() {
        int adapterInput = super.outPut220V();
        int adapterOutput = adapterInput/44;
        System.out.println("使用适配器 输入:" + adapterInput+"V,输出:"+adapterOutput);
        return 0;
    }

}

测试代码:

public class Main {

    public static void main(String[] args) {
        Dc5 adapter = new PowerAdapter();
        adapter.outputDc5();
    }
}

上面这个代码就是我们通过电源转换头将220V转换成了5V。

对象适配器

对象适配器的原理就是通过组合来实现适配器功能。具体做法就是:让Adapter实现Target接口,然后内部持有Adaptee实例,然后再Target接口规定的方法内进行转换。

这里我们只需要修改适配器实现即可,其他和类适配器一致:

public class PowerAdapter implements Dc5 {

    private Ac220 ac220;

    public PowerAdapter(Ac220 ac220) {
        this.ac220 = ac220;
    }

    @Override
    public int outputDc5() {
        int adapterInput = ac220.outPut220V();
        int adapterOutput = adapterInput/44;
        System.out.println("使用适配器 输入:" + adapterInput+"V,输出:"+adapterOutput);
        return adapterInput;
    }

}

接口适配器

接口适配器的关注点于类适配器和对象适配器的关注点不太一样,类适配器和对象适配器着重于将系统存在的一个角色转换成目标接口所需内容,而接口适配器的使用场景是解决接口方法过多,如果直接实现接口,那么类会多出许多空实现的方法,导致类显得很臃肿。此时,使用接口适配器就能让我们只实现我们需要的接口方法,目标更清晰。

接口适配器的主要原理就是抽象类实现接口,并且空实现接口众多方法。

首先创建Target角色:

public interface Dc {
    int outputV5();

    int outputV12();
    
    int outputV15();
}

创建Adaptee角色220V电压类:

public class Ac220 {

    public int outPut220V() {
        int output = 220;
        System.out.println("电压220V");
        return output;
    }

}

创建Adapter适配器:这里实现接口,方法为空方法即可。

public class PowerAdapter implements Dc {

    private Ac220 ac220;

    public PowerAdapter(Ac220 ac220) {
        this.ac220 = ac220;
    }

    @Override
    public int outputV5() {
        return 0;
    }

    @Override
    public int outputV12() {
        return 0;
    }

    @Override
    public int outputV15() {
        return 0;
    }

}

客户端代码:我们可以在适配器里面直接编写相关实现,也可以创建实例的时候编写我们需要的方法,这样就不需要实现那么多空实现的接口方法了。

public class Main {

    public static void main(String[] args) {
        Dc adapter = new PowerAdapter(new Ac220()) {
            @Override
            public int outputV5() {
                int a = new Ac220().outPut220V();
                return a/5;
            }
        };

        int result = adapter.outputV5();
        System.out.println(result);
    }
}

优缺点

优点

  • 能提高类的透明性和复用性,现有的类复用但不需要改变。
  • 目标类和适配器类解耦,提高程序的拓展性。
  • 在很多场景中符合开闭原则

缺点

  • 适配器编写过程需要全面考虑,可能会增加系统的复杂性。
  • 增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱。

相关文章:

  • 计算机网络原理 谢希仁(第8版)第四章习题答案
  • Linux入门第三天——linux命令(二)
  • 为什么要在单片机程序中使用结构体和指针
  • ROS1云课→19仿真turtlebot(stage)
  • VL1_四选一多路器(完整RTL、Testbench和覆盖率)
  • 【fiddler学习笔记】——安装、原理、使用
  • Idea无法引入@Test 或@Test引入报错【BUG解决】
  • Java中常见包装类型Integer、BigDecimal等特点说明
  • 渗透测试-apt攻击与防御系列-利用WinRAR跨目录获取Net-NTLM Hash和DLL劫持
  • MySQL的多表查询
  • Linux下udev应用
  • Responder的使用
  • 【小月电子】FPGA开发板(XLOGIC_V1)系统学习教程-LESSON6
  • JAVA和JVM和JDK和JRE和JAVA SE 是什么? 他们有什么区别? 怎么区分 编程下哪个?
  • Hydro-bzoj,你用过吗?
  • [iOS]Core Data浅析一 -- 启用Core Data
  • 「前端早读君006」移动开发必备:那些玩转H5的小技巧
  • 【mysql】环境安装、服务启动、密码设置
  • ➹使用webpack配置多页面应用(MPA)
  • Apache Pulsar 2.1 重磅发布
  • C++11: atomic 头文件
  • electron原来这么简单----打包你的react、VUE桌面应用程序
  • ES学习笔记(10)--ES6中的函数和数组补漏
  • HomeBrew常规使用教程
  • isset在php5.6-和php7.0+的一些差异
  • JavaScript中的对象个人分享
  • JS字符串转数字方法总结
  • php面试题 汇集2
  • Redis的resp协议
  • VUE es6技巧写法(持续更新中~~~)
  • 记一次删除Git记录中的大文件的过程
  • 聊聊redis的数据结构的应用
  • 每天一个设计模式之命令模式
  • 前端每日实战:61# 视频演示如何用纯 CSS 创作一只咖啡壶
  • 实习面试笔记
  • LevelDB 入门 —— 全面了解 LevelDB 的功能特性
  • Salesforce和SAP Netweaver里数据库表的元数据设计
  • 带你开发类似Pokemon Go的AR游戏
  • 你学不懂C语言,是因为不懂编写C程序的7个步骤 ...
  • #我与Java虚拟机的故事#连载05:Java虚拟机的修炼之道
  • $(document).ready(function(){}), $().ready(function(){})和$(function(){})三者区别
  • $.ajax,axios,fetch三种ajax请求的区别
  • (09)Hive——CTE 公共表达式
  • (13)Latex:基于ΤΕΧ的自动排版系统——写论文必备
  • (14)Hive调优——合并小文件
  • (安卓)跳转应用市场APP详情页的方式
  • (每日持续更新)jdk api之FileFilter基础、应用、实战
  • (七)理解angular中的module和injector,即依赖注入
  • (一)VirtualBox安装增强功能
  • (原創) 如何刪除Windows Live Writer留在本機的文章? (Web) (Windows Live Writer)
  • .NET Standard 的管理策略
  • .NET 编写一个可以异步等待循环中任何一个部分的 Awaiter
  • .NET 常见的偏门问题
  • .NET 读取 JSON格式的数据
  • .NET/C# 项目如何优雅地设置条件编译符号?