设计模式——适配器设计模式(结构型)

article/2025/6/17 0:57:41

摘要

本文详细介绍了适配器设计模式,包括其定义、核心思想、角色、结构、实现方式、适用场景及实战示例。适配器模式是一种结构型设计模式,通过将一个类的接口转换成客户端期望的另一个接口,解决接口不兼容问题,提高系统灵活性和可复用性,符合“开闭原则”。文中还探讨了对象适配器和类适配器两种实现方式,以及如何结合策略模式动态选择适配器。

1. 适配器设计模式定义

适配器模式:将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。适配器就是“中间翻译层”,用一个类把“不兼容”的类封装起来,让它们看起来“兼容”,从而可以被别的类调用。

1.1.1. 适配器模式的核心思想

  • 解决接口不兼容问题:通过适配器,将一个类的接口“包装”成另一个接口,供客户端调用。
  • 提高系统的灵活性和可复用性:客户端无需修改现有代码即可调用新的类或组件。
  • 符合“开闭原则”:对扩展开放,对修改关闭。

1.1.2. 📌 核心点总结:

点位

含义

目标接口

客户端期望使用的接口。

适配者(Adaptee)

已有的类(接口不兼容)

适配器(Adapter)

封装 Adaptee,实现 Target 接口,实现“转换”逻辑。

1.1.3. 适配器模式的角色

角色

说明

目标接口 (Target)

客户端期望的接口。客户端通过这个接口与适配器交互。

需要适配的类 (Adaptee)

现有的接口或类,功能符合需求,但接口不兼容。

适配器 (Adapter)

实现目标接口,内部持有需要适配的类的实例,并通过调用其方法来实现目标接口。

2. 适配器设计模式结构

适配器模式包含如下角色:

  • Target:目标抽象类
  • Adapter:适配器类
  • Adaptee:适配者类
  • Client:客户类

适配器模式有对象适配器和类适配器两种实现:

2.1. 对象适配器:

2.2. 类适配器:

2.3. 时序图

3. 适配器设计模式实现方式

3.1. 对象适配器(Object Adapter)

实现方式:适配器类内部通过组合的方式,持有被适配者类(Adaptee)的实例。

特点

  • 适配器实现目标接口(Target),
  • 内部调用被适配者实例的方法实现目标接口的方法,
  • 适配灵活,适配器和被适配者解耦,
  • 适配器可以适配多个被适配者实例。

结构示例

// 目标接口
interface Target {void request();
}// 被适配者
class Adaptee {void specificRequest() {System.out.println("被适配者的具体请求");}
}// 适配器(对象适配器)
class Adapter implements Target {private Adaptee adaptee;public Adapter(Adaptee adaptee) {this.adaptee = adaptee;}@Overridepublic void request() {// 通过调用被适配者的方法完成目标接口功能adaptee.specificRequest();}
}

3.2. 类适配器(Class Adapter)

实现方式:适配器通过继承的方式,同时继承被适配者类(Adaptee),并实现目标接口(Target)。

特点

  • 适配器类是被适配者的子类,
  • 可以直接调用被适配者的受保护或公共方法,
  • 只能适配一个被适配者类(Java单继承限制),
  • 适配器和被适配者耦合度较高。

结构示例

// 目标接口
interface Target {void request();
}// 被适配者
class Adaptee {void specificRequest() {System.out.println("被适配者的具体请求");}
}// 适配器(类适配器)
class Adapter extends Adaptee implements Target {@Overridepublic void request() {// 直接调用父类的方法super.specificRequest();}
}

3.3. 适配器示例总结

特性

对象适配器

类适配器

适配方式

组合(持有被适配者实例)

继承(直接继承被适配者)

灵活性

高,可以动态切换适配对象

低,继承限制,固定继承一个类

耦合度

Java单继承限制

无限制

只能继承一个被适配者类

4. 适配器设计模式适合场景

4.1. ✅ 适合使用适配器设计模式的场景

使用场景

说明

对接多个第三方接口,接口风格不一致

比如接多个风控、支付、短信、物流等服务,不同厂商接口差异很大。→ 使用适配器封装不同厂商接口,统一成系统期望的接口。

封装老旧系统/遗留代码

老系统接口风格与新系统不一致,但又不能修改旧代码。→ 使用适配器包装旧接口,提供符合新接口的使用方式。

统一Controller参数处理逻辑

Spring MVC 中自定义参数解析器 HandlerMethodArgumentResolver,可以视为一种适配器,将 HTTP 请求参数适配成业务对象。

消息中间件适配

Kafka、RabbitMQ、RocketMQ 提供的消息结构不一致,可使用适配器封装统一消费接口。

统一日志、监控、埋点等系统接入方式

不同日志系统(如 Logback、Log4j、ELK)、监控平台(如 Prometheus、SkyWalking)API 不统一,使用适配器将其统一为系统内部日志接口。

兼容不同规则引擎或插件机制

接入 Drools、EasyRules、自研规则引擎,通过适配器统一规则执行接口。

跨平台资源访问

比如统一适配本地文件系统、FTP、OSS、MinIO 等多种文件服务的上传/下载接口。

4.2. ❌ 不适合使用适配器设计模式的场景

场景

原因

接口已经统一,只需调用不同实现

用策略模式或 Spring Bean 多实现注入更合适

功能非常简单,仅调用一行代码

直接调用原类,无需适配

高性能要求场景,不能增加中间层

适配器可能增加调用链层次

适配器维护成本高于直接重构原类

如果可控代码建议重构,而不是套一层适配器

4.3. 📌 适配器设计模式总结

项目

适配器设计模式适用

不适用

是否接口不兼容但需协同工作

✅ 是

❌ 否

是否需要复用现有类且不改代码

✅ 是

❌ 否

是否频繁变更需求接口

❌ 否

✅ 是

是否对性能极度敏感

❌ 否

✅ 是

是否适配类与目标接口差异大

❌ 否

✅ 是

5. 适配器设计模式实战示例

背景:风控系统中,有多个第三方风险评分服务接口(接口不统一),需要统一成系统期望的接口供业务调用。使用适配器模式实现不同第三方服务的适配。

5.1. 场景描述

  • 系统需要调用不同第三方风控服务接口(比如:AlphaRiskServiceBetaRiskService),它们方法名、参数不同。
  • 系统定义统一的风控评分接口RiskScoreService,所有第三方服务通过适配器实现该接口。
  • Spring管理适配器bean,业务直接调用统一接口。

5.2. 定义统一风控评分接口(目标接口)

public interface RiskScoreService {/*** 计算用户的风险评分* @param userId 用户ID* @return 风险评分分数,范围0-100*/int calculateRiskScore(String userId);
}

5.3. 第三方风控服务接口及实现(被适配者)

// 第三方A风险服务,接口不统一
public class AlphaRiskService {public double getUserRisk(String userId) {// 模拟调用第三方接口,返回0.0~1.0的风险概率return Math.random();}
}// 第三方B风险服务,接口不同
public class BetaRiskService {public int fetchRiskLevel(String userId) {// 返回风险等级 1~5,5最高风险return (int)(Math.random() * 5) + 1;}
}

5.4. 适配器实现统一接口

import org.springframework.stereotype.Component;// Alpha适配器,组合方式(对象适配器)
@Component
public class AlphaRiskAdapter implements RiskScoreService {@Autowiredprivate final AlphaRiskService alphaRiskService;@Overridepublic int calculateRiskScore(String userId) {double riskProb = alphaRiskService.getUserRisk(userId);// 将0.0~1.0风险概率转为0~100分return (int)(riskProb * 100);}
}// Beta适配器,组合方式
@Component
public class BetaRiskAdapter implements RiskScoreService {@Autowiredprivate final BetaRiskService betaRiskService;@Overridepublic int calculateRiskScore(String userId) {int riskLevel = betaRiskService.fetchRiskLevel(userId);// 将风险等级1~5映射为0~100分return (riskLevel - 1) * 25;}
}

5.5. 业务服务调用统一接口

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class RiskEvaluationService {@Autowiredprivate final RiskScoreService riskScoreService;// 自定义注解@Qualifier来选择注入哪个适配器。// @Qualifier("")// private final RiskScoreService riskScoreService;public void evaluateUserRisk(String userId) {int score = riskScoreService.calculateRiskScore(userId);System.out.println("用户 " + userId + " 的风险评分为:" + score);// 根据风险评分做后续风控策略处理...}
}

5.6. Spring配置说明

  • 你可以通过Spring配置或自定义注解@Qualifier来选择注入哪个适配器。
  • 或者用策略模式管理多个适配器,根据业务动态选择。

6. 适配器设计模式思考

6.1. 用策略模式管理多个适配器,根据业务动态选择(策略模式 + 适配器模式结合示例)。

下面给你一个策略模式 + 适配器模式结合的示例,用于风控系统中动态选择不同适配器实现。

6.1.1. 设计思路

  • 各个第三方风控服务适配成实现统一接口 RiskScoreService 的适配器。
  • 定义策略上下文 RiskScoreContext,根据业务传入的标识动态选择具体适配器(策略)执行。
  • Spring管理多个适配器Bean,使用 @Qualifier 或自定义注解区分。
  • 业务调用上下文,动态选择适配器执行。

6.1.2. 统一接口(适配器接口)

public interface RiskScoreService {int calculateRiskScore(String userId);
}

6.1.3. 2. 两个适配器实现(对象适配器)

import org.springframework.stereotype.Component;@Component("alphaAdapter")
public class AlphaRiskAdapter implements RiskScoreService {private final AlphaRiskService alphaRiskService = new AlphaRiskService();@Overridepublic int calculateRiskScore(String userId) {double riskProb = alphaRiskService.getUserRisk(userId);return (int) (riskProb * 100);}
}@Component("betaAdapter")
public class BetaRiskAdapter implements RiskScoreService {private final BetaRiskService betaRiskService = new BetaRiskService();@Overridepublic int calculateRiskScore(String userId) {int riskLevel = betaRiskService.fetchRiskLevel(userId);return (riskLevel - 1) * 25;}
}

6.1.4. 策略上下文类,注入所有适配器

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.util.Map;@Component
public class RiskScoreContext {private final Map<String, RiskScoreService> strategyMap;@Autowiredpublic RiskScoreContext(Map<String, RiskScoreService> strategyMap) {this.strategyMap = strategyMap;}/*** 根据key选择对应的适配器执行* @param strategyKey 适配器标识,如 "alphaAdapter"、"betaAdapter"* @param userId 用户ID* @return 风险评分*/public int calculate(String strategyKey, String userId) {RiskScoreService service = strategyMap.get(strategyKey);if (service == null) {throw new IllegalArgumentException("未知的风险评分策略:" + strategyKey);}return service.calculateRiskScore(userId);}
}

6.1.5. 业务调用示例

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class RiskEvaluationService {private final RiskScoreContext riskScoreContext;@Autowiredpublic RiskEvaluationService(RiskScoreContext riskScoreContext) {this.riskScoreContext = riskScoreContext;}public void evaluateUserRisk(String userId, String strategyKey) {int score = riskScoreContext.calculate(strategyKey, userId);System.out.println("使用策略[" + strategyKey + "],用户" + userId + "风险评分为:" + score);// 这里可根据score做风控决策处理}
}

6.1.6. 测试调用示例

// 假设有Spring Boot主程序启动后,调用如下:@Autowired
RiskEvaluationService evaluationService;public void test() {evaluationService.evaluateUserRisk("user123", "alphaAdapter");evaluationService.evaluateUserRisk("user456", "betaAdapter");
}

6.1.7. 说明

  • Spring会自动将所有实现了RiskScoreService接口的Bean注入到strategyMap中,key为Bean的名称(如alphaAdapterbetaAdapter)。
  • 业务调用时传入策略key,根据key动态选择对应适配器。
  • 这样便实现了“策略模式管理多个适配器,根据业务动态选择”的需求

博文参考

  • 适配器模式(Adapter Pattern) | design-patterns
  • https://refactoringguru.cn/design-patterns/adapter

http://www.hkcw.cn/article/vIBZHkireZ.shtml

相关文章

元胞自动机(Cellular Automata, CA)

一、什么是元胞自动机&#xff08;Cellular Automata, CA&#xff09; 元胞自动机&#xff08;CA&#xff09; 是一种基于离散时间、离散空间与规则驱动演化的动力系统&#xff0c;由 冯诺依曼&#xff08;John von Neumann&#xff09; 于1940年代首次提出&#xff0c;用于模…

华为OD机试真题——模拟消息队列(2025A卷:100分)Java/python/JavaScript/C++/C语言/GO六种最佳实现

2025 A卷 100分 题型 本文涵盖详细的问题分析、解题思路、代码实现、代码详解、测试用例以及综合分析; 并提供Java、python、JavaScript、C++、C语言、GO六种语言的最佳实现方式! 2025华为OD真题目录+全流程解析/备考攻略/经验分享 华为OD机试真题《模拟消息队列》: 目录 题…

Nacos 配置文件总结

Nacos 配置文件总结 文章目录 Nacos 配置文件总结1 、在 Nacos 服务端添加配置文件1. 启动Nacos Server。2. 新建配置文件。3. 发布配置集后&#xff0c;我们便可以在配置列表中查看相应的配置文件。4. 配置nacos数据库5. 运行 Nacos 容器6. 验证安装结果7. 配置验证 2 、在 Na…

一文读懂MCP模型上下文协议

前言&#xff1a;MCP&#xff08;Model Context Protocol&#xff0c;模型上下文协议&#xff09;作为一个全新的开源协议框架被提出&#xff0c;它试图重塑模型开发、集成与协作的方式。MCP让只能人机交互的大模型转化为了能够快速对接各类业务系统的生产力大脑。传统做法通常…

C#数字图像处理(一)

文章目录 1.C#图像处理基础1.1 Bitmap类1.2 Bitmapdata类1.3 Graphics类1.4 Image类 2.彩色图像灰度化1.提取像素法2.内存法3.指针法三种方法的比较4.灰度图像二值化&#xff1a; 3.相关链接 Bitmap类、 Bitmapdata类和 Graphics类是C#图像处理中最重要的3个类,如果要用C# 进行…

各种数据库,行式、列式、文档型、KV、时序、向量、图究竟怎么选?

慕然回首&#xff0c;发现这些年来涌现出了许多类型的数据库&#xff0c;今天抽空简单回顾一下&#xff0c;以便于后面用到时能快速选择。 1. 关系型数据库(行式) 关系型数据库&#xff08;RDBMS&#xff09;&#xff0c;我们常说的数据库就是指的关系型数据库。 它的全称是关…

uView UI的使用

1. uView UI 封装了request.get登方法请求&#xff0c;异步调用。 故需可以使用then, catch, finally uView UI. 是一个专为UniApp设计的跨平台UI框架, 注意这里是跨平台&#xff0c;也就是可以再IOS, ANDROID 机器上运行的框架 2. easycom 词面意思容易使用的组件 在使用…

win32相关(临界区)

临界区 每个线程都有自己的栈&#xff0c;而局部变量是存在在栈中的&#xff0c;这就意味着每个线程都有一份自己的”局部变量“&#xff0c;如果线程仅仅只是使用自己的”局部变量“那么就不会有线程安全问题&#xff0c;那如果多个线程使用一个全局变量呢&#xff1f; 我们用…

UE5蓝图暴露变量,在游戏运行时修改变量实时变化、看向目标跟随目标Find Look at Rotation、修改玩家自身弹簧臂

UE5蓝图中暴露变量&#xff0c;类似Unity中public一个变量&#xff0c;在游戏运行时修改变量实时变化 1&#xff0c;添加变量 2&#xff0c;设置变量的值 3&#xff0c;点开小眼睛&#xff0c;此变量显示在编辑器中&#xff0c;可以运行时修改 看向目标跟随目标Find Look at R…

112 Gbps 及以上串行链路的有效链路均衡

通道均衡已成为当今高速串行链路的关键机制。目前有许多均衡方案&#xff0c;例如发射机加重均衡、接收机CTLE&#xff08;连续时间线性均衡器&#xff09;、FFE&#xff08;前馈均衡器&#xff09;、DFE&#xff08;判决反馈均衡器&#xff09;和FEC&#xff08;前向纠错&…

【LLM相关知识点】关于LangChain框架学习简单整理(三)

【LLM相关知识点】关于LangChain框架学习简单整理&#xff08;三&#xff09; 一、核心模块和协作模式 参考极简LangChain智能体开发入门指南&#xff0c;LangChain官方文档 LangChain核心模块与功能&#xff1a; 核心模块功能描述关键技术点​模型I/O管理大模型输入输出&a…

基于CangjieMagic的RAG技术赋能智能问答系统

目录 引言 示例程序分析 代码结构剖析 导入模块解读 智能体配置详情 提示词模板说明 主程序功能解析 异步聊天功能实现 检索信息展示 技术要点总结 ollama 本地部署nomic-embed-text 运行测试 结语 引言 这段时间一直在学习CangjieMagic。前几天完成了在CangjieMa…

【速通RAG实战:进阶】18、如何利用LLM记忆功能,实现一对一的个性化服务

一、赛博记忆的本质:从数据到数字人格的进化 (一)核心概念与技术定位 赛博记忆(Cyber Memory)是通过AI技术将个人多源数据(文本、图像、生物特征等)转化为可交互的动态记忆系统,其终极目标是构建可进化的数字人格,实现记忆的存储、重构与智能响应。与传统数据存储不…

C++深入类与对象

在上一篇中提到了构造函数&#xff0c;那么这篇再来提一下构造函数&#xff0c;编译器自动生成的默认构造函数对于内置类型不做处理&#xff0c;自定义类型会调用它自己的构造函数。对于自己写的构造函数&#xff0c;之前是在函数体中初始化&#xff0c;当然不止这一种初始化&a…

创新型老年综合评估实训室建设方案:提升评估实训精准性

随着人口老龄化加剧&#xff0c;老年综合评估人才需求激增&#xff0c;建设创新型老年综合评估实训室&#xff0c;是提升评估实训精准性、培育专业人才的关键路径。点击获取实训室建设方案 一、建设背景与意义 &#xff08;一&#xff09;行业发展需求 1、老龄化社会对老年综…

凤凰古城举行端午抢鸭子活动 欢乐江中庆佳节

5月31日,湖南凤凰古城沱江上举行了一场备受期待的“抢鸭子”大赛。500只精心挑选的鸭子被投入江中,其中一些鸭子颈项间系着鲜艳红绸带,象征好运。随着一声悠长的吆喝声响起,早已准备好的参赛者们纷纷跃入清凉的江水中。现场瞬间变成了欢乐的海洋,游客和市民一同沉浸在浓厚…

【深度学习】16. Deep Generative Models:生成对抗网络(GAN)

Deep Generative Models&#xff1a;生成对抗网络&#xff08;GAN&#xff09; 什么是生成建模&#xff08;Generative Modeling&#xff09; 生成模型的主要目标是从数据中学习其分布&#xff0c;从而具备“生成”数据的能力。两个关键任务&#xff1a; 密度估计&#xff0…

卢伟冰:诋毁本身就是一种仰望 小米汽车成功之道

小米集团合伙人、总裁卢伟冰昨晚谈到了小米汽车的成功。他表示,无论是SU7的热销还是YU7获得更高关注和期待,都基于强大的产品力。小米以“十倍投入做一辆好车”的决心,赢得了千万网友的认可。卢伟冰强调,小米汽车的成功体现了小米的价值观、模式和方法论。他还引用了莫言的…

西安交大原校长徐通模逝世 沉痛悼念与缅怀

沉痛悼念并深切缅怀徐通模同志。中国共产党优秀党员、西安交通大学原校长、我国动力工程与工程热物理领域著名专家、西安交通大学能源与动力工程学院热能工程系徐通模教授,因病医治无效,于2025年5月31日下午14时16分在西安逝世,享年86岁。徐通模同志1939年11月生于江苏如皋,…

性能优化 - 理论篇:常见指标及切入点

文章目录 引言一、 Java 性能优化的核心思路二、为什么要度量&#xff1f;三、常用性能衡量指标详解3.1 吞吐量与响应速度3.2 响应时间的具体度量&#xff1a;平均响应时间与百分位数3.3 并发量3.4 秒开率&#xff08;页面秒开&#xff09;3.5 正确性&#xff08;功能可用性&am…