【深入设计模式】适配器模式—一切皆有可能

news2024/9/23 7:20:46

文章目录

  • 1. 适配器模式
    • 1.1 适配器模式简介
    • 1.2 适配器模式结构
    • 1.3 适配器模式示例
  • 2. 适配器模式在源码中的应用
  • 3. 总结

适配器这个词来源于硬件领域,是一个独立的硬件设备接口,允许硬件或电子接口与其它硬件或电子接口相连,比如常见的电源适配器,将额定220V电压转换为低功率设备所需要的电压电流如12V5A等。适配器模式也是基于这样的思想而出现的一种设计模式,适配器模式同样能够在原本功能不变的情况下,让使用者能够通过“适配器”在不同方法中正常使用原本已有的功能,也就是让新方法通过适配器适配了原本的功能。

1. 适配器模式

1.1 适配器模式简介

在项目开发中,经常会在我们自己的代码里面整合框架中已有的功能,但是框架中的方法又没有提供扩展的接口,于是这个时候适配器模式就派上了用场。适配器模式通过一个适配器,让原本不相关且不兼容的两个功能方法能够一起协同工作,而对于调用方来说不需要做过多的实现就能够在自己的接口中实现原本接口的功能。也就是说我们可以在不改变原有功能的前提下,使用适配器模式在我们自己的代码中对原功能进行实现和扩展。

1.2 适配器模式结构

适配器模式的结构比较简单,主要就是适配器 Adapter 和被适配的对象 Adaptee

  • Client:客户端调用接口
  • Adapter:适配器,用于适配对象
  • Adaptee:被适配的对象

在这里插入图片描述

适配器模式有类适配器和对象适配器两种方式,首先我们来看看类适配器方式,类适配器主要是使用继承的方式在适配器中调用被适配的对象,代码如下:

首先我们需要定义被适配的对象 Adaptee 和客户端接口及方法,而适配器类通过实现 Client 接口以及继承被适配类 Adaptee ,通过在 adapterMethod 方法中调用 Adaptee 类的 method() 方法来完成 Adaptee 对象的适配

// 被适配对象
public class Adaptee {
    public void method() {
        System.out.println("adaptee method.");
    }
}
// 客户端接口
public interface Client {
    void adapterMethod();
}
// 适配器
public class Adapter extends Adaptee implements Client {
    @Override
    public void adapterMethod() {
        super.method();
    }
}

客户端调用代码如下:

Client client = new Adapter();
client.adapterMethod();		// 控制台输出:adaptee method.

而对象适配器不需要适配器继承被适配对象,而是通过将被适配对象作为参数进行调用,代码如下:

//适配器
public class Adapter implements Client {
    private Adaptee adaptee;

    public Adapter() {
        this.adaptee = new Adaptee();
    }

    @Override
    public void adapterMethod() {
        adaptee.method();
    }
}

这时客户端调用代码就有点不同:

Client client = new Adapter();
client.adapterMethod();		// 控制台输出:adaptee method.

1.3 适配器模式示例

这种数据线相信大家在生活中都看到过,平时是 Micro 接口的,但是我们只需将苹果接口转换器插入 Micro 接口上,便能让这根数据线能够同样适用于苹果手机,这时便能用适配器模式来完成这一场景。这个时候我们的 Micro 接口数据线正常使用,并将其作为被适配对象来适用于苹果手机。定义接口 IphoneInterface 并定义手机充电方法 charge()。有了 Micro 接口数据线,也有了 iphone 充电的方法,这个时候就需要定义充电适配器 ChargeAdapter 将 Micro 数据线适配用于 iphone 充电。Micro 类和 IphoneInterface 接口定义如下:

public class Micro {
    public void charge(){
        System.out.println("Use the micro usb interface for charging.");
    }
}
public interface IphoneInterface {
    void charge();
}

首先我们类适配器的方式进行编码,这个时候要让适配器继承被适配类 Micro,代码如下:

public class ChargeAdapter extends Micro implements IphoneInterface {
    @Override
    public void charge() {
        System.out.println("iphone is charging.");
        super.charge();
    }
}

客户端调用方法及控制台输出如下:

客户端调用方法
    IphoneInterface iphone = new ChargeAdapter();
    iphone.charge();

控制台打印:
    iphone is charging.
    Use the micro usb interface for charging.

接下来我们使用对象适配器来编码,区别在于适配器 ChargeAdapter 不继承被适配的类,而是将该对象作为参数进行调用:

public class ChargeAdapter extends Micro implements IphoneInterface {

    private Micro micro;

    public ChargeAdapter() {
        this.micro = new Micro();
    }

    @Override
    public void charge() {
        System.out.println("iphone is charging.");
        micro.charge();
    }
}

客户端调用方法及控制台输出如下:

客户端调用方法
    IphoneInterface iphone = new ChargeAdapter();
    iphone.charge();

控制台打印:
    iphone is charging.
    Use the micro usb interface for charging.

可以看到我们的 iphone 最后通过适配器都能够使用 Micro 接口的数据线进行充电了。看到这里肯定很多同学存在疑惑,这适配器模式咋跟装饰模式这么像(不了解装饰模式的话可以参考这篇博文:【深入设计模式】装饰模式—什么是装饰模式?装饰模式在源码中的应用)尤其是对象适配的方式?确实两种模式的写法非常相似,但是我们需要注意的时候装饰模式注重功能的增强,而适配器模式更注重于调用的转换。换句话说装饰模式是在已有功能上进行增强,而增强后的对象和被增强的对象是同一类的,也就是拥有相同父类;而适配器模式从客户端来看是通过适配器将两个毫不相关的对象进行关联,比如上面例子的用 Micro 数据线给 iphone 充电。

2. 适配器模式在源码中的应用

在 JDK 中我们经常使用 Set 集合,而我们如果看到源码便知道其实 JDK 中没有 Set 这种数据结构而是通过 Map 来实现的。以 HashSet 为例,在 HashSet 源码中维护了一个 HashMap 变量 map,而在调用 HashSet 构造方法是对该 map 进行初始化,我们再看添加元素、删除元素等常用方法时,均是在调用 map 对象的方法。从这里便能很明显的看出 HashSet 使用了适配器模式,HashSet 实际上就是一个适配器,被适配的对象就是 HashMap。

public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable
{
    static final long serialVersionUID = -5024744406713321676L;

    private transient HashMap<E,Object> map;

    public HashSet() {
        map = new HashMap<>();
    }
    
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }
    
    public boolean contains(Object o) {
        return map.containsKey(o);
    }
    
    public boolean remove(Object o) {
        return map.remove(o)==PRESENT;
    }
    
    public int size() {
        return map.size();
    }
    ……
}

3. 总结

适配器模式的整体思想和代码都比较简单,当我们需要在一个类中使用另外的类对象方法时,就可以考虑使用适配器模式,这样客户端在使用时便能够统一接口。适配器模式在日常开发中还是非常常见的,然而从某种意义上来讲,随着项目迭代,适配器模式其实是一种弥补措施,让新的接口兼容使用老的接口方式,因此我们应当在新接口以及被适配对象都不会发生太多变动的时候再考虑使用适配器模式,并且过多的使用适配器模式会让整体代码变得凌乱。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

(附源码)SSM芜湖公共书房服务平台 毕业设计 250859

SSM芜湖公共书房服务平台 摘 要 一座有底蕴的城市&#xff0c;应该是一个阅读的城市&#xff1b;一个有魅力的城市&#xff0c;应该是一个散发着书香的城市&#xff0c;而全民阅读量逐年增加&#xff0c;是社会进步、文明程度提高的重要标志。各大城市启动“全民阅读”工作以来…

Redis缓存过期和和内存淘汰策略

目录 1、MaxMemory 2、Expire数据结构 3、删除策略 3.1、惰性删除 3.2、主动删除 3.3、缓存淘汰策略 ​​​​​​​3.4、缓存淘汰策略的选择 1、MaxMemory Redis作为DB使用时&#xff0c;为了保证数据的完整性&#xff0c;不允许淘汰任何键值对。Redis作为缓存使用时&…

汇编语言与微机原理 期末半开卷复习整理(上)

8086CPU寄存器 8086&#xff1a;16位&#xff0c;4.77MHz~10MHz,16根数据线&#xff0c;20根地址线 AX/AL:乘除法指令中用作累加器&#xff0c;IO指令中用作数据寄存器&#xff0c;可显式或隐含调用 AH&#xff1a;在LAHF用作目的寄存器&#xff0c;隐含调用。 AL&#xff1a;…

大数据必学Java基础(一百一十一):过滤器注解应用和开发案例

文章目录 过滤器注解应用和开发案例 一、过滤器注解应用 二、开发案例

Python学习日记-第三十八天-生成器(第二节)

系列文章目录 使用greenlet&#xff0c;gevent完成多任务一、使用greenlet&#xff0c;gevent完成多任务 这里要先在pycharm里面提前安装好greenlet和gevent的包 操作&#xff1a; 代码&#xff1a; from greenlet import greenlet import timedef test1():while True:prin…

Android入门第39天-系统设置Configuration类

简介 本节给大家介绍的Configuration类是用来描述手机设备的配置信息的&#xff0c;比如屏幕方向&#xff0c; 触摸屏的触摸方式等。 Configuration给我们提供的方法列表 densityDpi&#xff1a;屏幕密度fontScale&#xff1a;当前用户设置的字体的缩放因子hardKeyboardHidd…

[深度学习] python基础支持汇总

这个系列放一些看神经网络源码过程中的python语法现象 文章目录前言一、list操作://extends/append的区别1.引入2.细致场景再现前言 例如&#xff1a;这个系列放一些看神经网络源码过程中的python语法现象, 直接解析语法太干瘪无聊.希望用这个方式来巩固所学知识 一、list操作…

电商评论文本情感分类(中文文本分类+中文词云图)(第一部分-textcnn)

电商评论文本情感分类(中文文本分类中文词云图) 第一部分 第二部分Bert部分 本项目包含&#xff1a; 1.中文文本处理 2.中文词云图绘制 3.中文词嵌入 4.基于textcnn的中文文本分类&#xff08;Test_Acc89.2000&#xff09; 5.基于bert的中文文本分类&#xff08;Test_Acc0.…

ServiceComb场景及其原理

文章目录Java-ChassisEnableServiceComb初始化SCBSPIServiceUtils自定义SPI加载器职责链管理器FilterChainsManager/ConsumerHandlerManagerRpcSchema注册服务如何保活&#xff1f;RpcReferencePropertySourcesPlaceholderConfigurerThreadPoolExecutorEx/LinkedBlockingQueueE…

深刻理解JAVA并发中的有序性问题和解决之道

问题 Java并发情况下总是会遇到各种意向不到的问题&#xff0c;比如下面的代码&#xff1a; int num 0;boolean ready false; // 线程1 执行此方法 public void actor1(I_Result r) {if(ready) {r.r1 num num;} else {r.r1 1;} } // 线程2 执行此方法 public void actor…

Clickhouse 使用DBeaver连接

ClickHouse是一个用于联机分析(OLAP)的列式数据库管理系统(DBMS)。 据处理大致可以分成两大类&#xff1a;联机事务处理OLTP&#xff08;on-line transaction processing&#xff09;、联机分析处理OLAP&#xff08;On-Line Analytical Processing&#xff09;。 OLTP是传统的…

JavaWeb--JDBC核心技术

JavaWeb--JDBC核心技术JDBC核心技术第1章&#xff1a;JDBC概述1.1 数据的持久化1.2 Java中的数据存储技术1.3 JDBC介绍1.4 JDBC体系结构1.5 JDBC程序编写步骤第2章&#xff1a;获取数据库连接2.1 要素一&#xff1a;Driver接口实现类2.1.1 Driver接口介绍2.1.2 加载与注册JDBC驱…

Redis学习笔记(六)

哨兵 哨兵时一个分布式系统&#xff0c;用于对主从结构中的每台服务器进行监控&#xff0c;当出现故障时通过投票机制选择新的master&#xff0c;并将所有slave连接到新的master哨兵的作用 监控 不断检查master和slave是否正常运行master存活检测、master与slave运行情况检测 通…

Linux调度(三)——抢占式调度

目录 抢占式场景一&#xff1a; 抢占式场景二 抢占的时机 用户态的抢占时机 抢占式机一&#xff1a; 抢占时机二&#xff1a; 内核态的抢占时机 时机一 时机二 总结 之前讲了主动式调度&#xff0c;就是进程运行到一半&#xff0c;因为等待I/O等操作而主动让出CPU&a…

动态规划算法(3)(不同方案数问题+拆分问题)

文章目录不同路径不同路径II整数拆分不同的二叉搜索树动态规划解题五步走&#xff1a; 确定dp数组以及下标的含义确定递推公式dp数组如何初始化确定遍历顺序举例推导dp数组 不同路径 力扣传送门&#xff1a; https://leetcode.cn/problems/unique-paths/description/ 确定dp…

[附源码]JAVA毕业设计酒店订房系统(系统+LW)

[附源码]JAVA毕业设计酒店订房系统&#xff08;系统LW&#xff09; 目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&…

基于opencv答题卡识别基本处理_1

文章目录1.读取图片2.图片预处理2.1 原图转换为灰度图2.2 高斯滤波处理&#xff0c;去除噪声点2.3 增强亮度2.4 自适应二值化2.5 图片可视化3. 添加边框3.1 使用copyMakeBorder添加边框3.2 可视化图片查看效果3.3 手动截取答题卡区域1.读取图片 img cv2.imread(images/5.png)…

Nx C++程序使用spdlog库进行日志存储

1 spdlog简介 spdlog是一个开源的日志库&#xff0c;在github上有。代码见这里&#xff0c;文档这里 C语言的&#xff0c;支持Linux、windows等系统。 csdn上也有许多介绍&#xff0c;这里列举两个&#xff1a;1、2 2 使用 2.1下载编译链接 有多种使用方式&#xff0c;这里…

(三)沟通管理风险管理采购管理@相关方管理

沟通管理目录概述需求&#xff1a;设计思路实现思路分析1.沟通管理绩效报告提供资源2.管理沟通3.监督沟通风险管理规划风险管理识别风险定性风险分析&#xff1a;定量分析风险规划风险应对实施分享应对监督风险采购管理&#xff1a;12.1 规划采购的管理12.2 实施采购控制采购相…

ResNet网络的改进版:ResNeXt

之前的文章讲过ResNet网络的基本架构&#xff0c;其本质就是让网络的学习目的从学习转为学习&#xff0c;也就是学习输入和输出之间的残差信息&#xff0c;从而缓解了梯度消失和网络退化问题。 本文讲下ResNet网络的改进版&#xff1a;ResNeXt。 架构 下面是ResNet和ResNeXt的架…