深入理解設計模式之適配器模式

深入理解設計模式之適配器模式

1. 適配器模式概述

適配器模式(Adapter Pattern)是一種結構型設計模式,它允許將一個類的接口轉換為客戶端所期望的另一個接口。適配器模式使得原本由于接口不兼容而不能一起工作的類能夠協同工作,扮演了"轉換器"的角色。

2. 適配器模式的核心組成

  • 目標接口(Target): 客戶端所期望的接口
  • 適配者(Adaptee): 需要被適配的類或接口
  • 適配器(Adapter): 將適配者接口轉換為目標接口的類

3. 適配器模式的類型

3.1 對象適配器

通過組合方式實現,適配器持有適配者的實例。

3.2 類適配器

通過繼承方式實現,適配器同時繼承適配者和實現目標接口。

4. 適配器模式實現

4.1 對象適配器實現

// 目標接口
interface MediaPlayer {void play(String audioType, String fileName);
}// 適配者接口
interface AdvancedMediaPlayer {void playVlc(String fileName);void playMp4(String fileName);
}// 具體適配者實現
class VlcPlayer implements AdvancedMediaPlayer {@Overridepublic void playVlc(String fileName) {System.out.println("Playing vlc file: " + fileName);}@Overridepublic void playMp4(String fileName) {// 不做任何事}
}class Mp4Player implements AdvancedMediaPlayer {@Overridepublic void playVlc(String fileName) {// 不做任何事}@Overridepublic void playMp4(String fileName) {System.out.println("Playing mp4 file: " + fileName);}
}// 適配器
class MediaAdapter implements MediaPlayer {private AdvancedMediaPlayer advancedMusicPlayer;public MediaAdapter(String audioType) {if (audioType.equalsIgnoreCase("vlc")) {advancedMusicPlayer = new VlcPlayer();} else if (audioType.equalsIgnoreCase("mp4")) {advancedMusicPlayer = new Mp4Player();}}@Overridepublic void play(String audioType, String fileName) {if (audioType.equalsIgnoreCase("vlc")) {advancedMusicPlayer.playVlc(fileName);} else if (audioType.equalsIgnoreCase("mp4")) {advancedMusicPlayer.playMp4(fileName);}}
}// 客戶端
class AudioPlayer implements MediaPlayer {private MediaAdapter mediaAdapter;@Overridepublic void play(String audioType, String fileName) {// 播放mp3格式音頻文件的內置支持if (audioType.equalsIgnoreCase("mp3")) {System.out.println("Playing mp3 file: " + fileName);}// 通過適配器播放其他格式else if (audioType.equalsIgnoreCase("vlc") || audioType.equalsIgnoreCase("mp4")) {mediaAdapter = new MediaAdapter(audioType);mediaAdapter.play(audioType, fileName);} else {System.out.println("Invalid media. " + audioType + " format not supported");}}
}// 測試類
public class AdapterPatternDemo {public static void main(String[] args) {AudioPlayer audioPlayer = new AudioPlayer();audioPlayer.play("mp3", "beyond_the_horizon.mp3");audioPlayer.play("mp4", "alone.mp4");audioPlayer.play("vlc", "far_far_away.vlc");audioPlayer.play("avi", "mind_me.avi");}
}

4.2 類適配器實現

// 目標接口
interface Target {void request();
}// 適配者類
class Adaptee {public void specificRequest() {System.out.println("Specific request from Adaptee");}
}// 類適配器
class ClassAdapter extends Adaptee implements Target {@Overridepublic void request() {specificRequest(); // 調用父類方法}
}// 測試類
public class ClassAdapterDemo {public static void main(String[] args) {Target target = new ClassAdapter();target.request();}
}

5. 真實世界適配器模式示例

5.1 舊系統集成案例

// 舊支付系統接口
class LegacyPaymentSystem {public void processPayment(String amount, String account) {System.out.println("Legacy payment processed: " + amount + " from account: " + account);}
}// 新支付接口
interface ModernPaymentGateway {void processPayment(Payment payment);
}// 支付數據傳輸對象
class Payment {private double amount;private String accountId;private String currency;public Payment(double amount, String accountId, String currency) {this.amount = amount;this.accountId = accountId;this.currency = currency;}public double getAmount() {return amount;}public String getAccountId() {return accountId;}public String getCurrency() {return currency;}
}// 支付系統適配器
class PaymentSystemAdapter implements ModernPaymentGateway {private LegacyPaymentSystem legacySystem;public PaymentSystemAdapter() {this.legacySystem = new LegacyPaymentSystem();}@Overridepublic void processPayment(Payment payment) {// 轉換和適配String amount = payment.getAmount() + " " + payment.getCurrency();legacySystem.processPayment(amount, payment.getAccountId());}
}// 客戶端代碼
public class PaymentSystemDemo {public static void main(String[] args) {// 創建新的支付請求Payment payment = new Payment(100.50, "ACC123456", "USD");// 使用適配器處理支付ModernPaymentGateway paymentGateway = new PaymentSystemAdapter();paymentGateway.processPayment(payment);}
}

5.2 第三方庫集成案例

// 第三方圖表庫接口
class ThirdPartyChartLibrary {public void displayChart(String data, String type, int width, int height) {System.out.println("Displaying " + type + " chart with data: " + data);System.out.println("Dimensions: " + width + "x" + height);}
}// 應用程序預期的圖表接口
interface AppChartInterface {void drawChart(ChartData data);
}// 圖表數據
class ChartData {private String dataPoints;private ChartType type;private Dimension size;public ChartData(String dataPoints, ChartType type, Dimension size) {this.dataPoints = dataPoints;this.type = type;this.size = size;}public String getDataPoints() {return dataPoints;}public ChartType getType() {return type;}public Dimension getSize() {return size;}
}// 圖表類型枚舉
enum ChartType {BAR, LINE, PIE
}// 尺寸類
class Dimension {private int width;private int height;public Dimension(int width, int height) {this.width = width;this.height = height;}public int getWidth() {return width;}public int getHeight() {return height;}
}// 圖表庫適配器
class ChartLibraryAdapter implements AppChartInterface {private ThirdPartyChartLibrary chartLibrary;public ChartLibraryAdapter() {this.chartLibrary = new ThirdPartyChartLibrary();}@Overridepublic void drawChart(ChartData data) {// 轉換數據格式String chartType = data.getType().toString().toLowerCase();chartLibrary.displayChart(data.getDataPoints(),chartType,data.getSize().getWidth(),data.getSize().getHeight());}
}// 客戶端代碼
public class ChartAdapterDemo {public static void main(String[] args) {// 創建圖表數據ChartData barChartData = new ChartData("10,25,30,40,50",ChartType.BAR,new Dimension(800, 400));// 使用適配器繪制圖表AppChartInterface chartInterface = new ChartLibraryAdapter();chartInterface.drawChart(barChartData);}
}

6. 雙向適配器模式

// 接口A
interface SystemA {void operationA();
}// 接口B
interface SystemB {void operationB();
}// 系統A實現
class ConcreteSystemA implements SystemA {@Overridepublic void operationA() {System.out.println("System A: performing operation A");}
}// 系統B實現
class ConcreteSystemB implements SystemB {@Overridepublic void operationB() {System.out.println("System B: performing operation B");}
}// 雙向適配器
class TwoWayAdapter implements SystemA, SystemB {private SystemA systemA;private SystemB systemB;public TwoWayAdapter(SystemA systemA, SystemB systemB) {this.systemA = systemA;this.systemB = systemB;}@Overridepublic void operationA() {System.out.println("Adapter: redirecting operationA to operationB");systemB.operationB();}@Overridepublic void operationB() {System.out.println("Adapter: redirecting operationB to operationA");systemA.operationA();}
}// 測試雙向適配器
public class TwoWayAdapterDemo {public static void main(String[] args) {SystemA systemA = new ConcreteSystemA();SystemB systemB = new ConcreteSystemB();// 創建雙向適配器TwoWayAdapter adapter = new TwoWayAdapter(systemA, systemB);// 通過適配器調用SystemA的功能System.out.println("Client uses SystemA interface:");adapter.operationA();// 通過適配器調用SystemB的功能System.out.println("\nClient uses SystemB interface:");adapter.operationB();}
}

7. Java標準庫中的適配器模式

Java標準庫中有多處使用了適配器模式:

import java.io.*;
import java.util.*;public class JavaAdapterExamples {public static void main(String[] args) {// 示例1: InputStreamReader作為適配器// InputStream(適配者) -> Reader(目標接口)try {InputStream is = new FileInputStream("file.txt");Reader reader = new InputStreamReader(is, "UTF-8");int data = reader.read();while(data != -1) {System.out.print((char) data);data = reader.read();}reader.close();} catch (Exception e) {e.printStackTrace();}// 示例2: Arrays.asList()作為適配器// 數組(適配者) -> List(目標接口)String[] namesArray = {"John", "Jane", "Adam"};List<String> namesList = Arrays.asList(namesArray);// 使用List接口的方法System.out.println(namesList.get(1));System.out.println(namesList.contains("Jane"));// 示例3: Collections.enumeration()作為適配器// Collection(適配者) -> Enumeration(目標接口)List<String> list = new ArrayList<>();list.add("Item 1");list.add("Item 2");Enumeration<String> enumeration = Collections.enumeration(list);while(enumeration.hasMoreElements()) {System.out.println(enumeration.nextElement());}}
}

8. 適配器模式的優缺點

優點

  • 解耦合: 客戶端與被適配者解耦,通過目標接口進行交互
  • 復用性: 可以復用現有的類,即使接口不匹配
  • 靈活性: 可以在不修改原有代碼的情況下使用已有功能
  • 開閉原則: 符合"開閉原則",不修改原有代碼而擴展功能

缺點

  • 復雜性: 引入額外的類,增加系統復雜度
  • 性能開銷: 可能引入額外的間接調用,影響性能
  • 可讀性: 過多的適配器會使系統難以理解

9. 適配器模式的適用場景

  • 集成遺留系統: 當需要集成舊系統或第三方庫時
  • 接口不兼容: 當現有接口與客戶端期望的接口不兼容時
  • 類庫遷移: 當遷移到新的類庫但不想修改現有代碼時
  • 多態接口: 當需要統一多個類的接口時

10. 適配器模式與其他模式的比較

模式適配器模式橋接模式裝飾器模式代理模式
意圖使不兼容接口兼容將抽象與實現分離動態添加功能控制對象訪問
結構轉換接口分離層次結構包裝對象包裝對象
側重點接口轉換接口與實現分離功能擴展訪問控制

11. 總結

適配器模式是一種強大的結構型設計模式,用于解決接口不兼容問題。它在系統集成、類庫遷移和接口統一等場景中非常有用。通過將一個類的接口轉換為客戶端期望的另一個接口,適配器讓原本不兼容的類能夠協同工作。

適配器模式的兩種主要實現方式——對象適配器和類適配器,為不同場景提供了靈活的解決方案。在實際開發中,對象適配器因其更高的靈活性和更低的耦合度而被更廣泛使用。

適當使用適配器模式可以大大提高代碼的可維護性和可擴展性,特別是在處理遺留系統和第三方庫集成時。但也應注意避免過度使用適配器,以免增加系統的復雜性。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/82148.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/82148.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/82148.shtml

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

相關文章

【數據結構 · 初階】- 快速排序

目錄 一. Hoare 版本 1. 單趟 2. 整體 3. 時間復雜度 4. 優化&#xff08;搶救一下&#xff09; 4.1 隨機選 key 4.2 三數取中 二. 挖坑法 格式優化 三. 前后指針&#xff08;最好&#xff09; 四. 小區間優化 五. 改非遞歸 快速排序是 Hoare 提出的一種基于二叉樹…

第2周 PINN核心技術揭秘: 如何用神經網絡求解偏微分方程

1. PDEs與傳統數值方法回顧 (Review of PDEs & Traditional Numerical Methods) 1.1 什么是偏微分方程 (Partial Differential Equations, PDEs)? 偏微分方程是描述自然界和工程領域中各種物理現象(如熱量傳播、流體流動、波的振動、電磁場分布等)的基本數學語言。 1.…

Neo4j(二) - 使用Cypher操作Neo4j

文章目錄 前言一、Cypher簡介二、數據庫操作1. 創建數據庫2. 查看數據庫3. 刪除數據庫4. 切換數據庫 三、節點、關系及屬性操作1. 創建節點與關系1.1 語法1.2 示例 2. 查詢數據2.1 語法2.2 示例 3. 更新數據3.1 語法3.2 示例 4. 刪除節點與關系4.1 語法4.2 示例 5. 合并數據5.1…

RabbitMQ的Web管理頁面給我看懵了,這都什么意思啊

文章目錄 OverviewTotalsMessage RatesQueued Messages NodesChurn StatisticsPorts and ContextsExport DefinitionsImport Definitions ConnectionsChannelsExchangesQueuesAdmin他們之間的關聯 在上一篇文章中我們講到了如何在Windows中安裝Rabbitmq&#xff0c; 小白也能搞…

安全基礎與協議分析

5.1 Web安全基礎 5.1.1 Web安全基礎概覽&#xff08;一、二&#xff09; Web安全的核心目標是保護Web應用及其數據免受攻擊&#xff0c;涵蓋以下關鍵領域&#xff1a; 攻擊面&#xff1a; 前端漏洞&#xff08;XSS、CSRF&#xff09;。 后端漏洞&#xff08;SQL注入、RCE&a…

STM32項目實戰:ADC采集

STM32F103C8T6的ADC配置。PB0對應的是ADC1的通道8。在標準庫中&#xff0c;需要初始化ADC&#xff0c;設置通道&#xff0c;時鐘&#xff0c;轉換模式等。需要配置GPIOB的第0腳為模擬輸入模式&#xff0c;然后配置ADC1的通道8&#xff0c;設置轉換周期和觸發方式。 接下來是I2C…

第十四章:數據治理之數據源:數據源的數據接入、業務屬性梳理及監控

本章開始&#xff0c;將進入9大模塊的介紹。第一個模塊我們先介紹&#xff1a;數據源。數據源是整個數據中臺數據的來源&#xff0c;是一個起點。更好的管理好數據源這個起點&#xff0c;是數據治理的一個好的開始。 在【數據&#xff1a;業務生數據&#xff0c;數據生“萬物”…

【C/C++】多線程開發:wait、sleep、yield全解析

文章目錄 多線程開發&#xff1a;wait、sleep、yield全解析1 What簡要介紹詳細介紹wait() — 條件等待&#xff08;用于線程同步&#xff09;sleep() — 睡覺&#xff0c;定時掛起yield() — 自愿讓出 CPU 2 區別以及建議區別應用場景建議 3 三者協作使用示例 多線程開發&#…

阿里云CDN刷新預熱--刷新URL

文章目錄 一、全英文URL刷新預熱二、摻雜中文的URL刷新預熱2.1 對帶中文URL進行編碼2.2 預熱刷新 三、CDN刷新-核心作用與價值3.1 核心作用3.2 核心價值3.3 典型使用場景 *最后我想說&#xff1a;請你不要相信我說的每一句話&#xff0c;這只是我的個人經驗* 一、全英文URL刷新…

Oracle 19c DG備庫報錯ORA-00313、ORA-00312、ORA-27037

Oracle 19c DG備庫報錯ORA-00313、ORA-00312、ORA-27037 錯誤排查問題處理錯誤排查 DG同步完成后,DG Broker show database發現以下告警信息: Database Warning(s):ORA-16826: apply service state is inconsistent with the DelayMins propertyORA-16789: standby redo log…

開源與閉源之爭:AI時代的創新博弈與未來抉擇

在人工智能技術狂飆突進的今天&#xff0c;開源與閉源之爭已不再局限于技術圈的討論&#xff0c;而是演變為一場關乎技術倫理、商業格局乃至人類文明走向的深度博弈。當Meta的Llama 3開源模型下載量突破百萬&#xff0c;當OpenAI的GPT-5繼續加固技術壁壘&#xff0c;這場沒有硝…

NIFI的處理器:JSLTTransformJSON 2.4.0

該處理器使用JSLT轉換FlowFile JSON有效負載的格式。使用轉換后的內容創建新的FlowFile&#xff0c;并將其路由到“成功”關系。如果JSLT轉換失敗&#xff0c;則將原始FlowFile路由到“失敗”關系。 需要注意的是&#xff0c;編譯JSLT轉換可能相當昂貴。理想情況下&#xff0c…

MySQL 索引失效及其解決辦法

一、前言 在數據庫優化中,索引(Index)是一項至關重要的技術手段,可以顯著提升查詢性能。然而,在實際開發過程中,MySQL 索引并不總是如預期生效。本文將從原理出發,系統地介紹索引失效的常見場景及其解決方案,幫助開發者有效規避性能陷阱。 二、索引基礎回顧 MySQL 支…

趨勢觸發策略

趨勢觸發策略(TS版)是一種基于TrendTriggerFactor(TTF)的交易策略,通過柱狀圖顏色變化指示市場趨勢的強度,并根據TTF的穿越信號進行買賣操作。 TTF是通過計算買方力量和賣方力量的差值除以兩者之和的一半再乘以100得到的。 當TTF大于100時,柱狀圖顯示為綠色,表示市場…

DeepSeek-R1 模型現已在亞馬遜云科技上推出

亞馬遜云科技提供眾多免費云產品&#xff0c;可以訪問&#xff1a;亞馬遜云科技 在剛剛過去的 Amazon re&#xff1a;Invent 期間&#xff0c;Amazon 首席執行官 Andy Jassy 分享了從 Amazon 自己在全公司開發近 1000 個生成式 AI 應用程序的經驗中汲取的寶貴經驗。從這種廣泛…

中臺項目-微前端qiankun-umimax

學習視頻&#x1f50a; 基礎&#xff1a; 黑馬前端基于qiankun搭建微前端項目實戰教程_嗶哩嗶哩_bilibili 路由、部署配置注意&#xff1a;qiankunvite微前端上線注意事項&#xff0c;base公共路徑設置_嗶哩嗶哩_bilibili 微前端 什么是微前端&#xff1f; 微前端是將前端應…

【Java學習筆記】代碼塊

代碼塊 介紹&#xff1a;代碼塊又稱為初始化塊&#xff0c;屬于類中的成員&#xff08;即是類的一部分&#xff09;&#xff0c;類似于方法&#xff0c;將邏輯語句封裝在方法體中&#xff0c;通過{}包圍起來 與類方法的不同點 無方法名 無返回類型 無參數 只有方法體&#…

spring boot 2.7集成舊的springfox-boot-starter swagger oas 3.0

舊版本目前已經不維護推薦使用 springdoc-openapi-ui&#xff0c;這里為了演示使用舊的最新依賴 <dependency><groupId>io.springfox</groupId><artifactId>springfox-boot-starter</artifactId><version>3.0.0</version> </dep…

Linux按鍵驅動測試方式詳細介紹

Linux按鍵驅動測試可采用以下分層方法&#xff1a; 基礎事件檢測 使用輸入子系統調試工具&#xff1a; sudo apt install evtest # 安裝事件測試工具 evtest # 選擇對應設備編號觸發按鍵后觀察終端輸出&#xff0c;正常情況應顯示&#xff1a; Event:…

USB學習【13】STM32+USB接收數據過程詳解

目錄 1.官方的描述2.HAL的流程把接收到的數據從PMA拷貝到用戶自己定義的空間中 3.處理接收到的數據4.最后再次開啟準備接收工作 1.官方的描述 2.HAL的流程 以上的官方說法我們暫時按下不表。 如果接收到數據&#xff0c;會激活中斷進入到USB_LP_CAN1_RX0_IRQHandler&#xff0…