摘要
本文詳細介紹了適配器設計模式,包括其定義、核心思想、角色、結構、實現方式、適用場景及實戰示例。適配器模式是一種結構型設計模式,通過將一個類的接口轉換成客戶端期望的另一個接口,解決接口不兼容問題,提高系統靈活性和可復用性,符合“開閉原則”。文中還探討了對象適配器和類適配器兩種實現方式,以及如何結合策略模式動態選擇適配器。
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 中自定義參數解析器 |
消息中間件適配 | Kafka、RabbitMQ、RocketMQ 提供的消息結構不一致,可使用適配器封裝統一消費接口。 |
統一日志、監控、埋點等系統接入方式 | 不同日志系統(如 Logback、Log4j、ELK)、監控平臺(如 Prometheus、SkyWalking)API 不統一,使用適配器將其統一為系統內部日志接口。 |
兼容不同規則引擎或插件機制 | 接入 Drools、EasyRules、自研規則引擎,通過適配器統一規則執行接口。 |
跨平臺資源訪問 | 比如統一適配本地文件系統、FTP、OSS、MinIO 等多種文件服務的上傳/下載接口。 |
4.2. ? 不適合使用適配器設計模式的場景
場景 | 原因 |
接口已經統一,只需調用不同實現 | 用策略模式或 Spring Bean 多實現注入更合適 |
功能非常簡單,僅調用一行代碼 | 直接調用原類,無需適配 |
高性能要求場景,不能增加中間層 | 適配器可能增加調用鏈層次 |
適配器維護成本高于直接重構原類 | 如果可控代碼建議重構,而不是套一層適配器 |
4.3. 📌 適配器設計模式總結
項目 | 適配器設計模式適用 | 不適用 |
是否接口不兼容但需協同工作 | ? 是 | ? 否 |
是否需要復用現有類且不改代碼 | ? 是 | ? 否 |
是否頻繁變更需求接口 | ? 否 | ? 是 |
是否對性能極度敏感 | ? 否 | ? 是 |
是否適配類與目標接口差異大 | ? 否 | ? 是 |
5. 適配器設計模式實戰示例
背景:風控系統中,有多個第三方風險評分服務接口(接口不統一),需要統一成系統期望的接口供業務調用。使用適配器模式實現不同第三方服務的適配。
5.1. 場景描述
- 系統需要調用不同第三方風控服務接口(比如:
AlphaRiskService
、BetaRiskService
),它們方法名、參數不同。 - 系統定義統一的風控評分接口
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的名稱(如alphaAdapter
、betaAdapter
)。 - 業務調用時傳入策略key,根據key動態選擇對應適配器。
- 這樣便實現了“策略模式管理多個適配器,根據業務動態選擇”的需求
博文參考
- 適配器模式(Adapter Pattern) | design-patterns
- https://refactoringguru.cn/design-patterns/adapter