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

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

摘要

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

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

相关文章:

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

摘要 本文详细介绍了适配器设计模式&#xff0c;包括其定义、核心思想、角色、结构、实现方式、适用场景及实战示例。适配器模式是一种结构型设计模式&#xff0c;通过将一个类的接口转换成客户端期望的另一个接口&#xff0c;解决接口不兼容问题&#xff0c;提高系统灵活性和…...

小黑大语言模型通过设计demo进行应用探索:langchain中chain的简单理解demo

chain简介 LangChain 中的 Chain 模块‌在开发大型语言模型&#xff08;LLM&#xff09;驱动的应用程序中起着至关重要的作用。Chain是串联LLM能力与实际业务的关键桥梁&#xff0c;通过将多个工具和模块按逻辑串联起来&#xff0c;实现复杂任务的多步骤流程编排。 案例 通过…...

秒杀系统—5.第二版升级优化的技术文档三

大纲 8.秒杀系统的秒杀库存服务实现 9.秒杀系统的秒杀抢购服务实现 10.秒杀系统的秒杀下单服务实现 11.秒杀系统的页面渲染服务实现 12.秒杀系统的页面发布服务实现 8.秒杀系统的秒杀库存服务实现 (1)秒杀商品的库存在Redis中的结构 (2)库存分片并同步到Redis的实现 (3…...

[SC]SystemC在CPU/GPU验证中的应用(六)

SystemC在CPU/GPU验证中的应用(六) 摘要:下面分享50个逐步升级SystemC编程能力的示例及建议的学习路线图。您可以一次一批地完成它们——从前五个基础的例子开始,然后转向channels, TLM, bus models, simple CPU/GPU kernels等等。在每个阶段掌握之后,再进行下一组…...

【STM32】HAL库 之 CAN 开发指南

基于stm32 f407vet6芯片 使用hal库开发 can 简单讲解一下can的基础使用 CubeMX配置 这里打开CAN1 并且设置好波特率和NVIC相关的配置 波特率使用波特率计算器软件 使用采样率最高的这段 填入 得到波特率1M bit/s 然后编写代码 环形缓冲区 #include "driver_buffer.h&qu…...

WPF的基础设施:XAML基础语法

XAML基础语法 1 控件声明与属性设置1.1 特性语法&#xff08;Attribute Syntax&#xff09;1.2 属性元素语法&#xff08;Property Element Syntax&#xff09;1.3 特殊值标记扩展 2 x:Name与Name的区别3 注释与代码折叠4 实用技巧集合5 常见错误排查 XAML( Extensible Applic…...

DeepSeek R1-0528 新开源推理模型(免费且快速)

DeepSeek推出了新模型,但这不是R2! R1-0528是DeepSeek的最新模型,在发布仅数小时后就在开源社区获得了巨大关注。 这个悄然发布的模型DeepSeek R1-0528,已经开始与OpenAI的o3一较高下。 让我来详细介绍这次更新的新内容。 DeepSeek R1-0528 发布 DeepSeek在这次发布中采…...

Go 语言的 GC 垃圾回收

序言 垃圾回收&#xff08;Garbage Collection&#xff0c;简称 GC&#xff09;机制 是一种自动内存管理技术&#xff0c;主要用于在程序运行时自动识别并释放不再使用的内存空间&#xff0c;防止内存泄漏和不必要的资源浪费。这篇文章让我们来看一下 Go 语言的垃圾回收机制是如…...

[git每日一句]your branch is behind ‘origin/master‘

当 Git 提示 "your branch is behind origin/master" 时&#xff0c;意思是&#xff1a; 你的本地分支落后于远程仓库&#xff08;origin&#xff09;的 master 分支 即&#xff1a;远程仓库有新的提交&#xff0c;而你的本地分支尚未同步这些更新。 如何解决&…...

【QT】在QT6中读取文件的方法

在QT6中读取文件的方法 QT6提供了多种读取文件的方式&#xff0c;下面我将介绍几种常用的方法&#xff0c;包括处理文本文件和二进制文件。 1. 使用QFile和QTextStream读取文本文件 这是读取文本文件最常用的方法&#xff1a; #include <QFile> #include <QTextSt…...

安全帽目标检测

安全帽数据集 这里我们使用的安全帽数据集是HelmentDetection&#xff0c;这是一个公开数据集&#xff0c;里面包含5000张voc标注格式的图像&#xff0c;分为三个类别&#xff0c;分别是 0: head 1: helmet 2: person 安全帽数据集下载地址、 我们将数据集下载后&#xff0c…...

Java工厂方法模式详解

工厂方法模式&#xff08;Factory Method Pattern&#xff09;是一种创建型设计模式&#xff0c;它将对象的创建和使用分离&#xff0c;通过定义一个创建对象的接口&#xff0c;让子类决定实例化哪个类。这种模式提高了代码的可扩展性和可维护性&#xff0c;尤其适用于需要根据…...

【pytorch学习】土堆pytorch学习笔记2

说明 主要以https://www.morinha.cc/posts/courses/pytorch-%E5%B0%8F%E5%9C%9F%E5%A0%86的内容为基础&#xff0c;没有的或者自己不是很清楚的再补充上内容&#xff0c;该贴有的内容大部分不再加入进来 新增的更全的参考&#xff1a; https://2048.csdn.net/6801fc28e9858151…...

Eclipse 插件开发 5.3 编辑器 监听输入

Eclipse 插件开发 5.3 编辑器监 听输入 1 插件配置2 添加监听3 查看效果 Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Click1 Bundle-SymbolicName: com.xu.click1;singleton:true Bundle-Version: 1.0.0 Bundle-Activator: com.xu.click1.Activator Bundle…...

iOS 集成网易云信IM

云信官方文档在这 看官方文档的时候&#xff0c;版本选择最新的V10。 1、CocoPods集成 pod NIMSDK_LITE 2、AppDelegate.m添加头文件 #import <NIMSDK/NIMSDK.h> 3、初始化 NIMSDKOption *mrnn_option [NIMSDKOption optionWithAppKey:"6f6568e354026d2d658a…...

Parasoft C++Test软件单元测试_实例讲解(对多次调用的函数打桩)

系列文章目录 Parasoft C++Test软件静态分析:操作指南(编码规范、质量度量)、常见问题及处理 Parasoft C++Test软件单元测试:操作指南、实例讲解、常见问题及处理 Parasoft C++Test软件集成测试:操作指南、实例讲解、常见问题及处理 进阶扩展:自动生成静态分析文档、自动…...

azure web app创建分步指南系列之二

为注册表授权托管标识 你创建的托管标识尚未获得从容器注册表中提取数据的授权。在此步骤中,你将启用授权。 返回容器注册表的管理页面: 在左侧导航菜单中,选择“访问控制 (IAM)”。选择“添加角色分配”。此屏幕截图显示了如何为容器注册表启用添加角色分配。在角色列表中…...

题海拾贝:P8598 [蓝桥杯 2013 省 AB] 错误票据

Hello大家好&#xff01;很高兴我们又见面啦&#xff01;给生活添点passion&#xff0c;开始今天的编程之路&#xff01; 我的博客&#xff1a;<但凡. 我的专栏&#xff1a;《编程之路》、《数据结构与算法之美》、《题海拾贝》 欢迎点赞&#xff0c;关注&#xff01; 1、题…...

MySQL 8.0:解析

引言 MySQL 8.0 作为里程碑版本&#xff0c;在功能、性能、安全性等维度进行了全面革新。以下从技术实现、应用场景和实践挑战三个层面&#xff0c;深度解析其核心特性变化&#xff1a; 一、架构级重构&#xff1a;数据字典与原子 DDL 1. 事务性数据字典 技术实现…...

Python量化交易12——Tushare全面获取各种经济金融数据

两年前写过Tushare的简单使用&#xff1a; Python量化交易08——利用Tushare获取日K数据_skshare- 现在更新一下吧&#xff0c;这两年用过不少的金融数据库&#xff0c;akshare&#xff0c;baostock&#xff0c;雅虎的&#xff0c;pd自带的......发现还是Tushare最稳定最好用&…...

封装一个小程序选择器(可多选、单选、搜索)

组件 <template><view class"popup" v-show"show"><view class"bg" tap"cancelMultiple"></view><view class"selectMultiple"><view class"multipleBody"><view class&…...

Dest建筑能耗模拟仿真功能简介

Dest建筑能耗模拟仿真功能简介 全球建筑能耗占终端能源消费的30%以上&#xff0c;掌握建筑能耗模拟是参与绿色建筑认证&#xff08;如LEED、WELL&#xff09;、超低能耗设计、既有建筑节能改造的必备能力。DEST作为国内主流建筑能耗模拟工具&#xff0c;广泛应用于设计院、咨询…...

【Hot 100】121. 买卖股票的最佳时机

目录 引言买卖股票的最佳时机我的解题 &#x1f64b;‍♂️ 作者&#xff1a;海码007&#x1f4dc; 专栏&#xff1a;算法专栏&#x1f4a5; 标题&#xff1a;【Hot 100】121. 买卖股票的最佳时机❣️ 寄语&#xff1a;书到用时方恨少&#xff0c;事非经过不知难&#xff01; 引…...

【机器学习基础】机器学习入门核心算法:XGBoost 和 LightGBM

机器学习入门核心算法&#xff1a;XGBoost 和 LightGBM 一、算法逻辑XGBoost (eXtreme Gradient Boosting)LightGBM (Light Gradient Boosting Machine) 二、算法原理与数学推导目标函数&#xff08;二者通用&#xff09;二阶泰勒展开&#xff1a;XGBoost 分裂点增益计算&#…...

Linux | Shell脚本的常用命令

一. 常用字符处理命令 1.1 连续打印字符seq seq打印数字&#xff1b;且只能正向打印&#xff0c;不可反向连续打印 设置打印步长 指定打印格式 1.2 反向打印字符tac cat 正向&#xff0c;tac 反向 1.3 打印字符printf printf "打印的内容"指定格式打印内容 换行…...

跑步的强度等级分类

概述 最大心率简化计算公式是【220-年龄】&#xff0c;具体值建议通过实际测试校准。在跑步训练中&#xff0c;以最大心率&#xff08;Heart Rate Maximum&#xff09;为指标对强度分类&#xff0c;常见分类对应的心率区间如下&#xff1a; 强度等级心率区间&#xff08;% HR…...

【JUC】深入解析 JUC 并发编程:单例模式、懒汉模式、饿汉模式、及懒汉模式线程安全问题解析和使用 volatile 解决内存可见性问题与指令重排序问题

单例模式 单例模式确保某个类在程序中只有一个实例&#xff0c;避免多次创建实例&#xff08;禁止多次使用new&#xff09;。 要实现这一点&#xff0c;关键在于将类的所有构造方法声明为private。 这样&#xff0c;在类外部无法直接访问构造方法&#xff0c;new操作会在编译…...

2025年全国青少年信息素养大赛复赛C++算法创意实践挑战赛真题模拟强化训练(试卷3:共计6题带解析)

2025年全国青少年信息素养大赛复赛C++算法创意实践挑战赛真题模拟强化训练(试卷3:共计6题带解析) 第1题:四位数密码 【题目描述】 情报员使用4位数字来传递信息,同时为了防止信息泄露,需要将数字进行加密。数据加密的规则是: 每个数字都进行如下处理:该数字加上5之后除…...

Mongodb | 基于Springboot开发综合社交网络应用的项目案例(中英)

目录 Project background Development time Project questions Create Project create springboot project project framework create folder Create Models user post Comment Like Message Serive tier user login and register Dynamic Publishing and Bro…...

飞腾D2000与FPGA结合的主板

UD VPX-404是基于高速模拟/数字采集回放、FPGA信号实时处理、CPU主控、高速SSD实时存储架构开发的一款高度集成的信号处理组合模块&#xff0c;采用6U VPX架构&#xff0c;模块装上外壳即为独立整机&#xff0c;方便用户二次开发。 UD VPX-404模块的国产率可达到100%&#xff0…...