一文彻底带你搞懂什么是适配器模式!!

一文彻底带你搞懂什么是适配器模式!!

    • 什么是适配器模式?
    • 适配器的两种实现方式
    • 适用情况
    • 代码示例
      • 背景
      • 类适配器
      • 对象适配器
    • IO流中的实际应用
        • 应用扩展
    • 总结

什么是适配器模式?

适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。

主要目的是解决在不改变现有代码的情况下,使不兼容的接口之间能够正常工作,通过创建一个中间转换的适配器来将一个对象转换成我们所需要的接口

例如下图的 USB 转 Type-C 就是一个适配器
在这里插入图片描述

适配器的两种实现方式

通常有两种方式实现适配器模式,一种是类适配器,类适配器目前已不太使用,另一种实现方式是对象适配器,通常情况下采用对象适配器会使得代码更易扩展与维护。

不管采用何种方式,其基本的实现思想都是:对现有接口的实现类进行扩展,使其实现客户期望的目标接口。

类适配器通过继承现有接口类并实现目标接口,这样的话会使得现有接口类完全对适配器暴露,使得适配器具有现有接口类的全部功能,破坏了封装性。此外从逻辑上来说,这也是不符合常理的,适配器要做的是扩展现有接口类的功能而不是替代。

对象适配器持有现有接口类一个实例,并扩展其功能,实现目标接口。这是推荐的方式,优先采用组合而不是继承,会使得代码更利于维护。

适用情况

  1. 接口不兼容:你有两个接口或类,它们原本不兼容,但你又想让它们能够一起工作。

  2. 希望复用代码:你已经有一个功能完整的类或接口,但接口不符合新的需求。通过适配器,你可以重用现有的代码,而不是重新实现一个新的版本。

  3. 第三方集成:你可能需要使用第三方库或类,但第三方库的接口与你自己的接口不兼容。使用适配器可以轻松地将第三方库集成到你的系统中。

代码示例

背景

美国的正常供电电压为110V,一个中国人带了一款中国制造电器去美国,这个电器必须要在220V电压下才能充电使用。

这种情况下,客户(中国人)的期望接口是有一个220V的电压为电器充电,但实际的接口是仅有一个110V的电压供电器充电,所以就需要采用一根电压转换器(适配器)使得110V的电压能够转换为220V的电压,供客户使用。

  • Target:客户期望获得的功能接口(220V电压供电)。
  • Cilent:客户,期望访问Target接口(客户期望能有220V电压)。
  • Adaptee:现有接口,这个接口需要被适配(现有110V电压供电,需要被适配至220V)。
  • Adapter:适配器类,适配现有接口使其符合客户需求接口(适配110V电压,使其变为220V电压)。

在适配器模式中,Adapter扩展Adaptee以实现对应功能,Cilent调用Adapter以获得相应功能。

类适配器

类适配器结构图:

在这里插入图片描述

详细附代码图:

在这里插入图片描述

// 定义一个目标接口,它声明了期望实现的充电方法chargeBy220V
interface Target {
    void chargeBy220V();
}

// 定义一个被适配的类接口,它有chargeBy110V充电方法
interface Adaptee {
    void chargeBy110V();
}

// 创建一个美国供电器类,它实现了被适配的类接口,提供110V充电方法
class AmericanCharger implements Adaptee {
    @Override
    public void chargeBy110V() {
        System.out.println("美国供电器,为您服务,正在给您充 110V 电压");
    }
}

// 创建一个适配器类,它继承了美国供电器类,并且实现了目标接口
class Adapter extends AmericanCharger implements Target {
    // 适配器重写目标接口的chargeBy220V方法,
    // 调用被适配类的chargeBy110V方法,并额外转换电压
    @Override
    public void chargeBy220V() {
        super.chargeBy110V(); // 调用被适配类的110V充电方法
        System.out.println("再加 110V,现在给您充 220V 电压!!"); // 实现额外的电压转换逻辑
    }
}

// 测试类,测试适配器模式
public class Client{
    public static void main(String[] args) {
        // 创建适配器对象,并通过目标接口调用chargeBy220V方法
        new Adapter().chargeBy220V();
    }
}

在这里插入图片描述

这种类适配器可能会让人误以为适配器本身就是提供110V充电功能的美国供电器,而忽略了它实际上是一个转换器,逻辑上容易混乱和让人混淆

对象适配器

在这里插入图片描述

在这里插入图片描述

/**
 * 定义一个目标接口Target,包含一个充电方法chargeBy220V,该方法用于充电220V电压。
 */
interface Target {
    void chargeBy220V(); // 定义接口方法,使用220V电压充电
}

/**
 * 定义一个适配者接口Adaptee,包含一个充电方法chargeBy110V,该方法用于充电110V电压。
 */
interface Adaptee {
    void chargeBy110V(); // 定义接口方法,使用110V电压充电
}

/**
 * 实现适配者接口Adaptee的AmericanCharger类,代表美国的充电器,提供110V的充电服务。
 */
class AmericanCharger implements Adaptee {
    @Override
    public void chargeBy110V() { // 覆写chargeBy110V方法,输出充电信息
        System.out.println("美国供电器,为您服务,正在给您充 110V 电压");
    }
}

/**
 * 实现目标接口Target的Adapter类,通过持有Adaptee对象来适配不同的电压充电需求。
 */
class Adapter implements Target {
    private Adaptee adaptee; // 私有变量,用于持有Adaptee类型的对象

    /**
     * 构造器,接收一个Adaptee对象以便在内部调用其方法。
     * @param adaptee 适配者对象
     */
    public Adapter(Adaptee adaptee) { // 构造器初始化适配器,并传入适配者对象
        this.adaptee = adaptee;
    }

    @Override
    public void chargeBy220V() { // 覆写chargeBy220V方法,实现适配功能
        adaptee.chargeBy110V(); // 首先使用110V充电
        System.out.println("再加 110V,现在给您充 220V 电压!!"); // 然后输出适配后的信息
    }
}

/**
 * 测试类Test,用于演示对象适配模式的使用。
 */
public class Client{
    public static void main(String[] args) {
    
        Adaptee adaptee = new AmericanCharger(); // 创建一个AmericanCharger对象

        Adapter adapter = new Adapter(adaptee); // 使用AmericanCharger对象来创建Adapter对象

        adapter.chargeBy220V(); // 通过Adapter对象调用chargeBy220V方法,输出适配后的充电信息
    }
}

在这里插入图片描述

对象适配器采用组合的方式实现对现有接口的扩展以达到客户期望的接口。

IO流中的实际应用

让我们来看JavaIO流中的一个实例:

FileInputStream fis = new FileInputStream("qe");
InputStreamReader isrAdapter = new InputStreamReader(fis);
BufferedReader bf = new BufferedReader(isrAdapter);

BufferedReader (此处也就是客户)需要读取文件字符流进行工作,读取文件字符流就是客户的需求部分,但是根据现有的接口,想要读取文件就只能读取字节流

FileInputStream 就是现有接口的一个具体实现类,为了满足客户的需求,我们要对现有的接口进行适配,

InputStreamReader 就是一个适配器,它持有一个现有接口类的实例,通过这个实例读取文件字节流并将其扩展为字符流以满足客户的需求,这是标准的对象适配器模式。

如果仔细研究源码,发现JavaIO库将适配器定义为抽象的,并由具体的适配器继承该抽象适配器,如这里的 InputStreamReader 就是具体的适配器之一。

应用扩展

如果实现适配有多种方式的话,我们可以将适配器类Adapter声明为抽象类,并由子类扩展它:

在这里插入图片描述

在这里插入图片描述

/**
 * 定义一个目标接口Target,
 * 包含一个充电方法chargeBy220V,该方法用于充电220V电压。
 */
interface Target {
    void chargeBy220V(); // 目标接口的充电方法,使用220V电压充电
}

/**
 * 定义一个适配者接口Adaptee,
 * 包含一个充电方法chargeBy110V,该方法用于充电110V电压。
 */
interface Adaptee {
    void chargeBy110V(); // 适配者接口的充电方法,使用110V电压充电
}

/**
 * 实现适配者接口Adaptee的AmericanCharger类,
 * 代表美国的充电器,提供110V的充电服务。
 */
class AmericanCharger implements Adaptee {
    @Override
    public void chargeBy110V() {
        System.out.println("美国供电器,为您服务,正在给您充 110V 电压");
    }
}

/**
 * 抽象类Adapter,作为适配器的基础类,实现目标接口Target。
 */
abstract class Adapter implements Target {
    Adaptee adaptee; // 私有变量,用于持有Adaptee类型的对象

    /**
     * 构造器,接收一个Adaptee对象以便在内部调用其方法。
     * @param adaptee 适配者对象
     */
    public Adapter(Adaptee adaptee) { // 构造器初始化适配器,并传入适配者对象
        this.adaptee = adaptee;
    }
}

/**
 * 实现Adapter类的具体子类ChinaMakeAdapter,
 * 用于将美国充电器的110V充电适配为中国标准的220V充电。
 */
class ChinaMakeAdapter extends Adapter{

    public ChinaMakeAdapter(Adaptee adaptee){
        super(adaptee); // 调用父类的构造器
    }

    /**
     * 实现目标接口Target的chargeBy220V方法,
     * 通过调用适配者接口Adaptee的chargeBy110V方法来实现。
     */
    @Override
    public void chargeBy220V() {
        adaptee.chargeBy110V(); // 调用适配者的110V充电方法
        System.out.println("再加110V,达到220V,认准中国制造!"); // 输出说明适配器正在工作
    }
}

/**
 * 测试类Client,用于演示对象适配器模式的使用。
 */
public class Client{
    public static void main(String[] args) {
    	// 创建一个AmericanCharger对象
        Adaptee adaptee = new AmericanCharger(); 
         // 使用AmericanCharger对象来创建ChinaMakeAdapter对象
        Adapter adapter = new ChinaMakeAdapter(adaptee);
        // 通过ChinaMakeAdapter对象调用chargeBy220V方法,输出适配后的充电信息
        adapter.chargeBy220V(); 
    }
}


在这里插入图片描述

总结

优点:

  1. 解耦合:适配器模式允许不相关的类或接口协同工作,这为系统的不同部分提供了更大的灵活性。
  2. 复用:通过适配器,可以复用现有的类,而不需要修改它们的源代码。
  3. 透明性:适配器提供了一种封装机制,允许客户端代码与目标接口交互,而不必关心实际的实现类。
  4. 灵活性:适配器模式提供了很大的灵活性,因为它允许在运行时动态地结合不同的类和接口。

缺点:

  1. 过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。
  2. 适配可能复杂或不可能:有时,适配两个不兼容的接口可能非常困难,甚至不可能,比如让母猪飞上天。

当我们有动机地修改一个正常运行的系统的接口,这时应该考虑使用适配器模式。

注意事项:适配器不是在详细设计时添加的,而是解决正在服役的项目的问题,即现有接口可能无法改变(去美国不可能把人家110V电压供给改成220V电压供给)。

往期设计模式专题文:

一文带你彻底搞懂什么是代理模式!!

一文带你彻底搞懂设计模式之单例模式!!由浅入深,图文并茂,超超超详细的单例模式讲解!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/783840.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

喂饭级AI语音生成神器Plus版来了!(懒人包)

之前有接触过数字人的朋友,都知道合成语音是制作数字人的关键一步。有不少AI工具可以合成语音,但要不就是收费的,要不就是在网页端使用,有隐私泄露风险。 之前给大家分享过一款网易有道开源的一款AI语音合成工具EmotiVoice&#…

全志A527 T527 设置左右分屏修改为单屏幕,应用分屏改为单屏

1.前言 android13中,A527的系统设置变成,左边是一级菜单,右侧是二级菜单, 这样跟我们以前android7/8/9的布局是不一样的,我们需要将它修改为一级菜单,点进去才是二级菜单这种。 效果如下 2.系统设置实现分析 它这里使用的是google新出的embedding activity, 相关的知…

林业气象站怎么选出专业设备?

随着全球气候变化的加剧,林业资源的保护与管理显得尤为重要。在选择林业气象站之前,首先要明确自身的需求。林业气象站的主要功能包括监测温度、湿度、风速、风向、降雨量等气象要素,但不同地区的林业生态环境存在差异,因此需要根…

vue-cli 脚手架详细介绍

4 vue-cli 脚手架 1 脚手架介绍 vue-cli也叫vue脚手架,vue-cli是vue官方提供的一个全局命令工具,这个命令可以帮助我们快速的创建一个vue项目的基础架子。 脚手架:搭建好的一个架子,我们在架子上进行开发 开箱即用零配置基于webpack、webpac…

为什么要学习Go

本文旨在探讨为什么Go语言值得学习,以及它如何能够提升您的编程技能和职业发展。我们将深入分析Go语言的核心优势,包括其简洁的语法、强大的并发支持、卓越的性能表现,以及在云计算、微服务和系统编程等领域的广泛应用 GO logo的核心理念,即简单胜于复杂。使用现代…

第10章:网络与信息安全

目录 第10章:网络与信息安全 网络概述 计算机网络概念 计算机网络的分类 网络的拓扑结构 ISO/OSI网络体系结构 网络互联硬件 物理层互联设备 数据链路层互联设备 网络层互联设备 应用层互联设备 网络的协议与标准 网络标准 TCP/IP协议族 网络接口层协…

git恢复到之前提交的记录

项目搞崩了,还提交上去了怎么办? 那当然是恢复到之前的提交记录了,那怎么操作呢? 首先,到代码托管平台找到你想恢复的提交记录(在此以github为例) 获取 commit id 首先,通过如下图操作获取到commit id {% a…

代发考生战报:南京考场华为售前HCSP H19-411考试通过

代发考生战报:南京考场华为售前HCSP H19-411考试通过,客服给的题库非常稳定,考试遇到2个新题,剩下全是题库里的原题,想考的放心考吧,考场服务挺好,管理员带着做签名和一些考试说明介绍清楚&…

简单介绍 Dagger2 的入门使用

依赖注入 在介绍 Dagger2 这个之前,必须先解释一下什么是依赖注入,因为这个库就是用来做依赖注入的。所以这里先简单用一句话来介绍一下依赖注入: 依赖注入是一种设计模式,它允许对象在运行时注入其依赖项。而不是在编译时确定&a…

我们所熟知的meme梗图也可以用AI生成了,老外都玩坏了。

meme梗图不知道大家看到过嘛?相信你们看见下面的图你就会大叫“卧槽”,原来是这种图,我以前经常狂刷不止,太有趣了。 其实meme是一个网络流行语,可译为模因。在大众非学术范围内也可翻译为我们所熟知的“梗”。其中“表…

Function Call ReACT,Agent应用落地的加速器_qwen的function calling和react有什么不同

探索智能体Agent的未来之路:Function Call与ReACT框架的较量,谁能引领未来? 引言 各大平台出现智能体应用创建,智能体逐渐落地,背后的使用哪种框架? 随着各大平台,例如百度千帆APPbuilder、阿…

分类下两列一组统计

表格 A 列是分类,后面是 2N 个 key-value 列 ABCDEFG1CountryLabel1Count1Label2Count2Label3Count32USA10B9C83USD9C8A74USC8D7B65USA7C6B56CAA10B9C87CAD9C8A78CAC8D7B69INA10C9B810IND9A8B711INA8D7B6 需要对分类、key 分组,对 value 求和&#xff…

一文揭秘:中小企业选择做软文营销推广的优势有哪些?

在当今这个信息泛滥、注意力稀缺的时代,中小企业作为经济活力的重要组成部分,面临着前所未有的机遇与挑战。而相比于硬广告的直接灌输,软文营销推广以其独特的魅力,成为了中小企业提升品牌形象、吸引目标客户的重要手段。今天投媒…

tqdm进度条函数使用 python

1.作用: 通过使用 tqdm ,可以让您在处理大量数据或长时间运行的循环时,更好地了解程序的执行进度,增强用户体验。 2.使用 ---可以使用 pip install tqdm 进行安装。 ---tqdm.tqdm(iterator可迭代对象,desc描述符&…

Linux下QT程序启动失败问题排查方法

文章目录 0.问题背景1.程序启动失败常见原因2.排查依赖库问题2.1 依赖库缺失2.2 依赖库加载路径错误2.3 依赖库版本不匹配2.4 QT插件库缺失2.4.1 QT插件库缺失2.4.2 插件库自身的依赖库缺失 2.5 系统基础C库不匹配 3.资源问题3.1 缺少翻译文件3.2 缺少依赖的资源文件3.3 缺少依…

视频融合共享平台LntonCVS视频监控汇聚平台工业视频监控系统

LntonCVS是一款功能强大、灵活部署的安防视频监控平台,具备广泛的扩展性和视频能力。它支持多种主流标准协议,如国标GB28181、RTSP/Onvif、RTMP,同时还能兼容厂家的私有协议和SDK,如海康Ehome、海大宇等。除了传统的安防监控功能外…

基于51单片机的四路抢答器Protues仿真设计

一、设计背景 近年来随着科技的飞速发展,单片机的应用正在不断的走向深入。本文阐述了基于51单片机的八路抢答器设计。本设计中,51单片机充当了核心控制器的角色,通过IO口与各个功能模块相连接。按键模块负责检测参与者的抢答动作&#xff0c…

ExcelVBA运用Excel的【条件格式】(二)

ExcelVBA运用Excel的【条件格式】(二) 前面知识点回顾 1. 访问 FormatConditions 集合 Range.FormatConditions 2. 添加条件格式 FormatConditions.Add 方法 语法 表达式。添加 (类型、 运算符、 Expression1、 Expression2) 3. 修改或删除条件…

【数据结构】线性表----栈详解

栈 栈(Stack)是一种常见的数据结构,它具有**后进先出(Last In, First Out, LIFO)**的特点。栈的运作类似于物理世界中的叠盘子:最新放上去的盘子最先被拿走,而最底部的盘子最后才能被取出。 如…

企业文档加密软件推荐丨2024企业用什么加密软件

在数字化时代,信息安全已经成为企业和个人不可忽视的问题。文档加密软件作为一种保护敏感信息不被非法访问或篡改的有效工具,其重要性日益凸显。通过加密技术,可以确保文档内容在传输和存储过程中的安全性,防止数据泄露和未经授权…