設計模式——適配器設計模式(結構型)

摘要

本文詳細介紹了適配器設計模式,包括其定義、核心思想、角色、結構、實現方式、適用場景及實戰示例。適配器模式是一種結構型設計模式,通過將一個類的接口轉換成客戶端期望的另一個接口,解決接口不兼容問題,提高系統靈活性和可復用性,符合“開閉原則”。文中還探討了對象適配器和類適配器兩種實現方式,以及如何結合策略模式動態選擇適配器。

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.pswp.cn/bicheng/83221.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/83221.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/83221.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

java 開發中 nps的內網穿透 再git 遠程訪問 以及第三放支付接口本地調試中的作用

在Java開發中&#xff0c;NPS內網穿透、Git遠程訪問和第三方支付接口的本地調試結合使用&#xff0c;可以有效提升開發效率和調試能力。以下是它們的具體作用及協作場景&#xff1a; 第一&#xff1a;為什么需要nps內網穿透 1. NPS內網穿透的作用 NPS&#xff08;內網穿透工具…

換ip是換網絡的意思嗎?怎么換ip地址

在數字化時代&#xff0c;IP地址作為我們在網絡世界的"身份證"&#xff0c;其重要性不言而喻。許多人常將"換IP"與"換網絡"混為一談&#xff0c;實際上兩者雖有聯系卻存在本質區別。本文將澄清這一概念誤區&#xff0c;并詳細介紹多種更換IP地址…

云游戲混合架構

云游戲混合架構通過整合本地計算資源與云端能力&#xff0c;形成了靈活且高性能的技術體系&#xff0c;其核心架構及技術特征可概括如下&#xff1a; 一、混合架構的典型模式 分層混合模式? 前端應用部署于公有云&#xff08;如渲染流化服務&#xff09;&#xff0c;后端邏輯…

Docker常用命令操作指南(一)

Docker常用命令操作指南-1 一、Docker鏡像相關命令1.1 搜索鏡像&#xff08;docker search&#xff09;1.2 拉取鏡像&#xff08;docker pull&#xff09;1.3 查看本地鏡像&#xff08;docker images&#xff09;1.4 刪除鏡像&#xff08;docker rmi&#xff09; 二、Docker容器…

軟件性能之CPU

性能是個宏大而駁雜話題&#xff0c;從代碼&#xff0c;到網絡&#xff0c;到實施&#xff0c;方方面面都會涉及到性能問題&#xff0c;網上對性能講解的文章多如牛毛&#xff0c;從原理到方法再到工具都有詳細的介紹&#xff0c;本文雖不能免俗&#xff0c;但期望能從另外一個…

[SC]SystemC在CPU/GPU驗證中的應用(三)

SystemC在CPU/GPU驗證中的應用(三) 摘要:下面分享50個逐步升級SystemC編程能力的示例及建議的學習路線圖。您可以一次一批地完成它們——從前五個基礎的例子開始,然后轉向channels, TLM, bus models, simple CPU/GPU kernels等等。在每個階段掌握之后,再進行下一組…

如何設計高效的數據湖架構:存儲策略、Schema 演進與數據生命周期管理

本文圍繞現代數據湖架構的核心設計理念與實踐展開,重點討論如何高效組織數據存儲、支持 Schema 演進與版本管理、實現冷熱數據分層存儲和生命周期治理,確保數據湖在性能、成本、演進和治理能力上的全面可控。 ?? 一、數據湖架構演進概覽 傳統數據倉庫面對高頻更新、Schema…

建筑兔零基礎人工智能自學記錄101|Transformer(1)-14

Transformer 谷歌提出&#xff0c;一組編碼-解碼器 可以同時處理&#xff0c;通過位置編碼來處理單詞 實質是token詞語接龍&#xff08;只是有不同的概率&#xff09; token對應向量 Transformer簡述 文生圖就需要用到transformer黑箱 token 內部層次 中間主要是embedding…

Unity基礎學習(十二)Unity 物理系統之范圍檢測

目錄 一、關于范圍檢測的主要API&#xff1a; 1. 盒狀范圍檢測 Physics.OverlapBox 2. 球形范圍檢測 Physics.OverlapSphere 3. 膠囊范圍檢測 Physics.OverlapCapsule 4. 盒狀檢測 NonAlloc 版 5. 球形檢測 NonAlloc 版 6. 膠囊檢測 NonAlloc 版 二、關于API中的兩個重…

構建安全高效的郵件網關ngx_mail_ssl_module

一、快速上手&#xff1a;最小配置示例 worker_processes auto;mail {server {# 監聽 IMAP over TLSlisten 993 ssl;protocol imap;# TLS 協議與密碼套件ssl_protocols TLSv1.2 TLSv1.3;ssl_ciphers HIGH:!aNULL:!MD5;# 證書與私鑰ssl_…

打卡day41

知識回顧 數據增強卷積神經網絡定義的寫法batch歸一化&#xff1a;調整一個批次的分布&#xff0c;常用與圖像數據特征圖&#xff1a;只有卷積操作輸出的才叫特征圖調度器&#xff1a;直接修改基礎學習率 卷積操作常見流程如下&#xff1a; 1. 輸入 → 卷積層 → Batch歸一化層…

MySQL高級查詢技巧:分組、聚合、子查詢與分頁【MySQL系列】

本文將深入探討 MySQL 高級查詢技巧&#xff0c;重點講解 GROUP BY、HAVING、各種聚合函數、子查詢以及分頁查詢&#xff08;LIMIT 語法&#xff09;的使用。文章內容涵蓋實際應用中最常見的報表需求和分頁實現技巧&#xff0c;適合有一定 SQL 基礎的開發者進一步提升技能。 一…

現代 CSS 高階技巧:實現平滑內凹圓角的工程化實踐

通過 數學計算 CSS mask 復合遮罩 實現的真正幾何內凹效果&#xff1a; 背景是一張圖片&#xff0c;用來證明中間的凹陷是透明的。 完整代碼&#xff1a; app.js import FormPage from "./pages/formPage"; import "./App.css"; const App () > {re…

Qt不同布局添加不同控件

對于這種 不同布局添加不同控件 的情況,可以采用以下幾種簡化方法: 方法 1:使用 std::pair 或 std::tuple 配對(C++17 推薦) for (auto [layout, widget] : {std::pair{m_layoutMistakeCalibrate,

MySQL 事務解析

1. 事務簡介 事務&#xff08;Transaction&#xff09; 是一組操作的集合&#xff0c;它是一個不可分割的工作單位&#xff0c;事務會把所有的操作作為一個整體一起向系統提交或撤銷操作請求&#xff0c;即這些操作要么同時成功&#xff0c;要么同時失敗。 經典案例&#xff1…

PyTorch中 torch.utils.data.DataLoader 的詳細解析和讀取點云數據示例

一、DataLoader 是什么&#xff1f; torch.utils.data.DataLoader 是 PyTorch 中用于加載數據的核心接口&#xff0c;它支持&#xff1a; 批量讀取&#xff08;batch&#xff09;數據打亂&#xff08;shuffle&#xff09;多線程并行加載&#xff08;num_workers&#xff09;自…

在MDK中自動部署LVGL,在stm32f407ZGT6移植LVGL-8.4,運行demo,顯示label

在MDK中自動部署LVGL&#xff0c;在stm32f407ZGT6移植LVGL-8.4 一、硬件平臺二、實現功能三、移植步驟1、下載LVGL-8.42、MDK中安裝LVGL-8.43、配置RTE4、配置頭文件 lv_conf_cmsis.h5、配置lv_port_disp_template 四、添加心跳相關文件1、在STM32CubeMX中配置TIM7的參數2、使能…

德思特新聞 | 德思特與es:saar正式建立合作伙伴關系

德思特新聞 2025年5月9日&#xff0c;德思特科技有限公司&#xff08;以下簡稱“德思特”&#xff09;與德國嵌入式系統專家es:saar GmbH正式達成合作伙伴關系。此次合作旨在將 es:saar 的先進嵌入式開發與測試工具引入中國及亞太市場&#xff0c;助力本地客戶提升產品開發效率…

fork函數小解

學了好久終于搞懂fork函數的一些作用 1. fork函數作用&#xff1a;用于創建新的子進程 這是fork最根本的功能&#xff0c;在父進程里創建新的子進程、 但是創建新的子進程之后呢&#xff1f; 子進程和父進程的關系是什么樣的&#xff1f; 為什么fork得到的子進程返回值為0&am…

opencv(C++) 變換圖像與形態學操作

文章目錄 使用腐蝕和膨脹圖像形態濾波器實現案例使用形態學濾波器對圖像進行開運算和閉運算實現案例在灰度圖像上應用形態學操作算子形態學梯度(Morphological Gradient)黑帽變換(Black-hat Transform)使用分水嶺算法進行圖像分割使用 MSER 提取顯著區域MSER 檢測與可視化使…