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

适配器模式(Adapter)

适配器模式用于将一个接口转换成用户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式

Adapter is a structural design pattern that transforms an interface into another interface for the users' desires 
and allows objects with incompatible interfaces to collaborate.

结构设计

适配器模式包含如下角色:
Target:目标类,描述了其他类与客户端代码合作时必须遵循的协议,简单来说,就是客户端使用的目标接口。
Adaptee:适配者类,待适配的类。客户端与其接口不兼容,因此无法直接调用其功能。
Adapter:适配器类,适配器(Adapter)是一个可以同时与客户端和服务交互的类:它在实现客户端接口的同时封装了服务对象。适配器接受客户端通过适配器接口发起的调用,并将其转换为适用于被封装服务对象的调用。
Client:客户端类,客户端代码只需通过接口与适配器交互即可, 无需与具体的适配器类耦合。 因此,开发者可以向程序中添加新类型的适配器而无需修改已有代码。这在服务类的接口被更改或替换时很有用:开发者无需修改客户端代码就可以创建新的适配器类。适配器模式有对象适配器和类适配器两种实现:
(1) 对象适配器:
请添加图片描述
(2) 类适配器:
请添加图片描述

伪代码实现

接下来将使用代码介绍下适配器模式的实现。首先是对象适配器模式的实现:

// 1、目标类,对客户端需要使用的目标接口进行声明
public interface Target {void request();
}// 2、适配者类,待适配类,其声明的接口无法被客户端直接调用
public class Adaptee {public void specialRequest() {System.out.println("---------do some thing in an adaptee instance---------");}
}// 3、适配器类,适配器可以接受客户端通过适配器接口发起的调用,并将其转换为适用于被封装服务对象的调用。  
// 对象适配器通过组合适配者实例并实现目标类来完成适配
public class Adapter implements Target {private Adaptee adaptee = new Adaptee();@Overridepublic void request() {adaptee.specialRequest();}
}// 4、客户端调用
public class AdapterClient {public void test() {Target adapter = new Adapter();adapter.request();}
}

其次是类适配器模式的实现:

// 1、目标类,对客户端需要使用的目标接口进行声明
public interface Target {void request();
}// 2、适配者类,待适配类,其声明的接口无法被客户端直接调用
public class Adaptee {public void specialRequest() {System.out.println("---------do some thing in an adaptee instance---------");}
}// 3、适配器类,适配器可以接受客户端通过适配器接口发起的调用,并将其转换为适用于被封装服务对象的调用。  
// 类适配器通过继承适配者实例并实现目标类来完成适配
public class Adapter extends Adaptee implements Target {@Overridepublic void request() {specialRequest();}
}// 4、客户端调用
public class AdapterClient {public void test() {Target adapter = new Adapter();adapter.request();}
}

从上面的实现不难发现,类适配器是基于继承实现,而对象适配器是基于组合关系实现。由于对象适配器是通过关联关系进行耦合的,因此在设计时更灵活,而类适配器就只能通过重写Adaptee的方法进行扩展。

适用场景

在以下情况下可以考虑使用适配器模式:
(1) 需要使用某个现有类,但是这些类的接口不符合系统的需要,可以考虑使用适配器。如以下场景:
适配器模式允许创建一个中间层类, 其可作为代码与遗留类、第三方类或提供接口的类之间的转换器。
如果需要复用这样一些类, 他们处于同一个继承体系, 并且他们又有了额外的一些共同的方法, 但是这些共同的方法不是所有在这一继承体系中的子类所具有的共性。
扩展每个子类,将缺少的功能添加到新的子类中,且无法将功能提取到父类。 但是, 必须在所有新子类中重复添加这些代码,这样会使得代码有坏味道。
(2) 想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类一起工作,可以考虑使用适配器。
(3) 将缺失功能添加到一个适配器类中是一种优雅的解决方案。 在这方案中,开发者可以将缺少功能的对象封装在适配器中, 从而动态地获取所需功能。 如要这一点正常运作, 目标类必须要有通用接口, 适配器的成员变量应当遵循该通用接口。 这种方式同装饰模式非常相似。

优缺点

适配器让接口不兼容的对象可以相互合作。适配器模式有以下优点:
(1) 单一职责。可以将接口或数据转换代码从程序主要业务逻辑中分离。
(2) 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,而无须修改原有代码。
(3) 增加了类的透明性和复用性,将具体的实现封装在适配者类中,对于客户端来说是透明的,而且提高了适配者的复用性。
(4) 灵活性和扩展性都非常好,通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,完全符合“开闭原则”。
类适配器模式还具有如下优点:
由于适配器类是适配者类的子类,因此可以在适配器类中置换一些适配者的方法,使得适配器的灵活性更强。
对象适配器模式还具有如下优点:
一个对象适配器可以把多个不同的适配者适配到同一个目标,也就是说,同一个适配器可以把适配者类和它的子类都适配到目标接口。
但是适配器模式也存在以下缺点:
(1) 代码整体复杂度增加, 因为开发者需要新增一系列接口和类。 有时直接更改服务类使其与其他代码兼容会更简单。
类适配器模式还具有如下缺点:
对于Java、C#等不支持多重继承的语言,一次最多只能适配一个适配者类,而且目标抽象类只能为抽象类,不能为具体类,其使用有一定的局限性,不能将一个适配者类和它的子类都适配到目标接口。
对象适配器模式还具有如下缺点:
与类适配器模式相比,要想置换适配者类的方法就不容易。如果一定要置换掉适配者类的一个或多个方法,就只好先做一个适配者类的子类,将适配者类的方法置换掉,然后再把适配者类的子类当做真正的适配者进行适配,实现过程较为复杂。

参考

《设计模式:可复用面向对象软件的基础》 Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides 著 李英军, 马晓星 等译
https://design-patterns.readthedocs.io/zh_CN/latest/structural_patterns/adapter.html 适配器模式
https://blog.csdn.net/ShuSheng0007/article/details/116161690 秒懂设计模式之适配器模式
https://www.runoob.com/design-pattern/adapter-pattern.html 适配器模式
https://www.cnblogs.com/adamjwh/p/9033549.html 简说设计模式——适配器模式
https://refactoringguru.cn/design-patterns/adapter 适配器模式

相关文章:

适配器模式(Adapter)

适配器模式用于将一个接口转换成用户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。 Adapter is a structural design pattern that…...

Spring学习笔记——1

Spring学习笔记——1 一、Spring入门1.1、学习路线1.2、传统Javaweb开发困惑及解决方法1.3、三种思想的提出和框架概念1.3.1、IoC、DI和AOP思想提出1.3.2、框架的基本特点 1.4、Spring概述1.5、BeanFactory快速入门1.6、ApplicationContext快速入门1.7、BeanFactory与Applicati…...

leetcode 406. 根据身高重建队列

2023.8.2 这题一开始有点让人懵逼的是有两个维度,一个是身高,还一个是前面人高于自己的人数。这种题一般需要先固定一个维度,再去确定另外一个维度,不要想着兼顾。 经过纸上模拟,我的思路是先通过身高进行从大到小排序…...

Matlab实现AGNES算法

在数据分析和机器学习中,聚类是一种常用的无监督学习方法,它可以将数据点按照某种相似度标准进行分组,从而发现数据中的结构和模式。聚类算法有很多种,其中一种比较经典的是AGNES算法,它是一种基于层次的聚类算法&…...

STM32F4_外部SRAM

目录 前言 1. SRAM控制原理 1.1 SRAM功能框图 1.2 SRAM读写时序 2. FSMC简介 2.1 FSMC架构 2.2 FSMC地址映射 2.3 FSMC控制SRAM时序 3. FSMC结构体 4. 库函数配置FSMC 5. 实验程序 5.1 main.c 5.2 SRAM.c 5.3 SRAM.h 前言 STM32F4自带了192K字节的SRAM&#xff1…...

Java的代理模式

java有三种代理模式 静态代理 jdk动态代理 cglib实现动态代理 代理模式的定义: 为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的…...

FilterAttributeOnClassMethod

目录 1 BadMethodFilterAttribute 2 FilterAttributeOnClassMethod 2.1 OnMethodExecuted 2.2 OnMethodExecutedAsync 2.3 OnMethodExecuting BadMethodFilterAttribute using System; using System.Threading.Tasks; namespace Flatwhite.Core.Tests.Attributes …...

springboot + (mysql/pgsql) + jpa 多数据源(不同类数据源)

配置文件: spring:datasource:primary:jdbc-url: jdbc:mysql://host:3306/数据库?useUnicodetrue&characterEncodingUTF-8&autoReconnecttrue&failOverReadOnlyfalse&serverTimezoneAsia/Shanghai&zeroDateTimeBehaviorconvertToNullusername…...

【Golang】Golang进阶系列教程--Go 语言 context 都能做什么?

文章目录 前言核心是 Context 接口:包含四个方法:遵循规则WithCancelWithDeadlineWithTimeoutWithValue 前言 很多 Go 项目的源码,在读的过程中会发现一个很常见的参数 ctx,而且基本都是作为函数的第一个参数。 为什么要这么写呢…...

画图干货!14种uml图类型及示例

1. 什么是 UML UML 是统一建模语言的缩写。UML 图是基于 UML(统一建模语言)的图表,目的是直观地表示系统及其主要参与者、角色、动作、工件或类,以便更好地理解、更改、维护或记录信息关于系统。简而言之,UML 是一种…...

计算机视觉实验:人脸识别系统设计

实验内容 设计计算机视觉目标识别系统,与实际应用有关(建议:最终展示形式为带界面可运行的系统),以下内容选择其中一个做。 1. 人脸识别系统设计 (1) 人脸识别系统设计(必做):根据…...

振弦采集仪完整链条的岩土工程隧道安全监测

振弦采集仪完整链条的岩土工程隧道安全监测 隧道工程是一种特殊的地下工程,其建设过程及运行期间,都受到各种内外力的作用,如水压、地震、地质变形、交通荷载等,这些因素都会对隧道的安全性产生影响。因此,对隧道的安…...

NLP实战9:Transformer实战-单词预测

目录 一、定义模型 二、加载数据集 三、初始化实例 四、训练模型 五、评估模型 🍨 本文为[🔗365天深度学习训练营]内部限免文章(版权归 *K同学啊* 所有) 🍖 作者:[K同学啊] 模型结构图: &a…...

使用Vue.js和Rust构建高性能的物联网应用

物联网(IoT)应用是现代技术的重要组成部分,它们可以在各种场景中(例如智能家居,工业自动化等)提供无缝的自动化解决方案。在这篇文章中,我们将探讨如何使用Vue.js和Rust构建高性能的物联网应用。 1. 为什么选择Vue.js…...

idea调节文字大小、日志颜色、git改动信息

idea调节菜单栏文字大小: 调节代码文字大小: 按住ctrl滚动滑轮可以调节代码文字大小: 单击文件即可在主窗口上打开显示: idea在控制台对不同级别的日志打印不同颜色 : “grep console”插件 点击某一行的时候&#x…...

避免大龄程序员边缘化:如何在技术行业中保持竞争力

目录 导语持续学习和进修维护专业形象寻找适合自己的领域构建个人品牌和网络拥抱变化和创新实例结语: 导语 导语:随着科技的不断发展,技术行业的竞争日益激烈。对于那些年龄稍长的程序员来说,如何保持竞争力并避免边缘化成为了一…...

Jenkins工具系列 —— 启动 Jenkins 服务报错

错误显示 apt-get 安装 Jenkins 后,自动启动 Jenkins 服务报错。 排查原因 直接运行jenkins命令 发现具体报错log:Failed to start Jetty或Failed to bind to 0.0.0.0/0.0.0.0:8080或Address already in use 说明:这里提示的是8080端口号…...

华为数通HCIA-实验环境ensp简介

ensp 路由器:AR系列、NE系列; 模拟器中使用AR2220; 交换机:S系列、CE系列; 模拟器中使用S5700; 线缆:copper——以太网链路; serial——串行链路,在模拟器中用于模…...

SK5代理与IP代理:网络安全中的爬虫利器

一、什么是IP代理与SK5代理? IP代理: IP代理是一种允许用户通过代理服务器进行网络连接的技术。用户请求经由代理服务器中转,从而实现隐藏真实IP地址,保护用户隐私,并在一定程度上突破IP访问限制。常见的IP代理有HTTP…...

实战:Prometheus+Grafana监控Linux服务器及Springboot项目

文章目录 前言知识积累什么是Prometheus什么是Grafana怎样完成数据采集和监控 环境搭建docker与docker-compose安装docker-compose编写 监控配置grafana配置prometheus数据源grafana配置dashboardLinux Host Metrics监控Spring Boot 监控 写在最后 前言 相信大家都知道一个项目…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻

在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...

【算法训练营Day07】字符串part1

文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接&#xff1a;344. 反转字符串 双指针法&#xff0c;两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...

用docker来安装部署freeswitch记录

今天刚才测试一个callcenter的项目&#xff0c;所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建

华为云FlexusDeepSeek征文&#xff5c;DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色&#xff0c;华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型&#xff0c;能助力我们轻松驾驭 DeepSeek-V3/R1&#xff0c;本文中将分享如何…...

Netty从入门到进阶(二)

二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架&#xff0c;用于…...

Java数值运算常见陷阱与规避方法

整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...

MySQL 部分重点知识篇

一、数据库对象 1. 主键 定义 &#xff1a;主键是用于唯一标识表中每一行记录的字段或字段组合。它具有唯一性和非空性特点。 作用 &#xff1a;确保数据的完整性&#xff0c;便于数据的查询和管理。 示例 &#xff1a;在学生信息表中&#xff0c;学号可以作为主键&#xff…...

离线语音识别方案分析

随着人工智能技术的不断发展&#xff0c;语音识别技术也得到了广泛的应用&#xff0c;从智能家居到车载系统&#xff0c;语音识别正在改变我们与设备的交互方式。尤其是离线语音识别&#xff0c;由于其在没有网络连接的情况下仍然能提供稳定、准确的语音处理能力&#xff0c;广…...

Linux中《基础IO》详细介绍

目录 理解"文件"狭义理解广义理解文件操作的归类认知系统角度文件类别 回顾C文件接口打开文件写文件读文件稍作修改&#xff0c;实现简单cat命令 输出信息到显示器&#xff0c;你有哪些方法stdin & stdout & stderr打开文件的方式 系统⽂件I/O⼀种传递标志位…...

Linux-进程间的通信

1、IPC&#xff1a; Inter Process Communication&#xff08;进程间通信&#xff09;&#xff1a; 由于每个进程在操作系统中有独立的地址空间&#xff0c;它们不能像线程那样直接访问彼此的内存&#xff0c;所以必须通过某种方式进行通信。 常见的 IPC 方式包括&#…...