【设计模式】适配器模式大白话讲解!
适配器模式大白话讲解
一句话概括
就像转接头,让不兼容的东西能够一起工作
现实生活比喻
场景1:电源转接头
- 问题:中国插头(扁的)插不进欧洲插座(圆的)
- 解决方案:用一个转接头,中国插头 → 转接头 → 欧洲插座
- 结果:完美使用!
场景2:翻译官
- 问题:中国人说中文,美国人说英文,互相听不懂
- 解决方案:请个翻译,中文 → 翻译 → 英文
- 结果:顺利沟通!
完整代码示例
场景:让旧式播放器支持新格式
/*** 适配器模式 - 媒体播放器示例*/
public class Main {public static void main(String[] args) {System.out.println("=== 传统MP3播放 ===");MediaPlayer mp3Player = new MP3Player();mp3Player.play("song.mp3");System.out.println("\n=== 通过适配器播放新格式 ===");MediaPlayer advancedPlayer = new MediaAdapter();// 现在可以播放各种格式了!advancedPlayer.play("movie.avi");advancedPlayer.play("video.mp4");advancedPlayer.play("animation.vlc");advancedPlayer.play("music.mp3");}
}/*** 目标接口 - 我们期望的播放器接口*/
interface MediaPlayer {void play(String fileName);
}/*** 已有类 - 只能播放MP3的老式播放器*/
class MP3Player implements MediaPlayer {@Overridepublic void play(String fileName) {if (fileName.endsWith(".mp3")) {System.out.println("播放MP3文件: " + fileName);} else {System.out.println("格式不支持: " + fileName);}}
}/*** 新功能接口 - 高级媒体播放器*/
interface AdvancedMediaPlayer {void playVlc(String fileName);void playMp4(String fileName);void playAvi(String fileName);
}/*** 新功能实现 - 具体的高级播放器*/
class AdvancedPlayer implements AdvancedMediaPlayer {@Overridepublic void playVlc(String fileName) {System.out.println("播放VLC文件: " + fileName);}@Overridepublic void playMp4(String fileName) {System.out.println("播放MP4文件: " + fileName);}@Overridepublic void playAvi(String fileName) {System.out.println("播放AVI文件: " + fileName);}
}/*** 适配器 - 核心!让老播放器支持新格式*/
class MediaAdapter implements MediaPlayer {private AdvancedMediaPlayer advancedPlayer;public MediaAdapter() {this.advancedPlayer = new AdvancedPlayer();}@Overridepublic void play(String fileName) {if (fileName.endsWith(".vlc")) {advancedPlayer.playVlc(fileName);} else if (fileName.endsWith(".mp4")) {advancedPlayer.playMp4(fileName);} else if (fileName.endsWith(".avi")) {advancedPlayer.playAvi(fileName);} else if (fileName.endsWith(".mp3")) {// 适配器也可以直接处理MP3,或者委托给MP3PlayerSystem.out.println("播放MP3文件: " + fileName);} else {System.out.println("格式不支持: " + fileName);}}
}
运行结果
=== 传统MP3播放 ===
播放MP3文件: song.mp3=== 通过适配器播放新格式 ===
播放AVI文件: movie.avi
播放MP4文件: video.mp4
播放VLC文件: animation.vlc
播放MP3文件: music.mp3
更真实的例子:数据库连接适配器
/*** 数据库连接适配器示例*/
public class DatabaseExample {public static void main(String[] args) {// 老系统用的MySQLDatabase mysql = new MySQLDatabase();mysql.connect();mysql.query("SELECT * FROM users");System.out.println("\n=== 系统升级,要支持新数据库 ===");// 新来的MongoDB不兼容老接口Database adapter = new MongoDBAdapter();adapter.connect();adapter.query("db.users.find()");}
}/*** 目标接口 - 老系统期望的数据库接口*/
interface Database {void connect();void query(String sql);
}/*** 已有类 - MySQL数据库*/
class MySQLDatabase implements Database {@Overridepublic void connect() {System.out.println("MySQL连接成功");}@Overridepublic void query(String sql) {System.out.println("执行SQL: " + sql);}
}/*** 需要适配的类 - MongoDB(不兼容的接口)*/
class MongoDB {public void mongoConnect() {System.out.println("MongoDB连接成功");}public void find(String command) {System.out.println("执行Mongo命令: " + command);}
}/*** 适配器 - 让MongoDB看起来像传统数据库*/
class MongoDBAdapter implements Database {private MongoDB mongoDB;public MongoDBAdapter() {this.mongoDB = new MongoDB();}@Overridepublic void connect() {mongoDB.mongoConnect();  // 适配:connect → mongoConnect}@Overridepublic void query(String sql) {// 把SQL转换成MongoDB的查询语法String mongoCommand = convertSQLToMongo(sql);mongoDB.find(mongoCommand);  // 适配:query → find}private String convertSQLToMongo(String sql) {// 简单的转换逻辑(实际中会很复杂)if (sql.startsWith("SELECT")) {return "db." + sql.substring(sql.indexOf("FROM") + 5) + ".find()";}return sql;}
}
适配器模式的三种形式
1. 类适配器(通过继承)
// 通过继承来实现适配(Java中不常用,因为单继承限制)
class ClassAdapter extends AdvancedPlayer implements MediaPlayer {@Overridepublic void play(String fileName) {if (fileName.endsWith(".mp4")) {playMp4(fileName);  // 直接调用父类方法}// ... 其他格式}
}
2. 对象适配器(通过组合,推荐)
// 通过组合来实现适配(更灵活)
class ObjectAdapter implements MediaPlayer {private AdvancedMediaPlayer advancedPlayer;  // 组合public ObjectAdapter() {this.advancedPlayer = new AdvancedPlayer();}@Overridepublic void play(String fileName) {// 委托给advancedPlayerif (fileName.endsWith(".mp4")) {advancedPlayer.playMp4(fileName);}}
}
3. 接口适配器(缺省适配器)
// 当接口方法太多时,提供默认空实现
abstract class MediaAdapter implements MediaPlayer {public void play(String fileName) {}  // 默认空实现public void stop() {}                 // 默认空实现public void pause() {}                // 默认空实现
}// 使用时只需要重写需要的方法
class SimplePlayer extends MediaAdapter {@Overridepublic void play(String fileName) {System.out.println("简单播放: " + fileName);}// 不需要实现stop和pause方法
}
适用场景(大白话版)
✅ 适合用适配器的场景:
- 
系统升级 // 老系统要接入新组件 OldSystem system = new OldSystem(); NewComponent adapter = new Adapter(newComponent); system.use(adapter); // 老系统无感知
- 
第三方库集成 // 第三方库接口不匹配 ThirdPartyLib lib = new ThirdPartyLib(); OurInterface adapter = new LibAdapter(lib); ourSystem.use(adapter);
- 
接口不兼容 // 两个系统接口不同 SystemA systemA = new SystemA(); SystemB adapter = new ABAdapter(systemA); client.use(adapter);
❌ 不适合的场景:
- 可以直接修改源码的情况
- 系统设计阶段,应该直接设计统一接口
- 过度设计,如果只是临时用一次,不如直接写兼容代码
优缺点
优点:
- 解耦:客户端和目标类解耦
- 复用:可以复用现有的类
- 灵活:可以适配多个不同的类
缺点:
- 复杂度:增加了系统的类和对象数量
- 性能:多了一层调用,有轻微性能损失
总结
适配器模式就是:
- 中间人,在两个不兼容的接口之间搭桥
- 翻译官,把一种语言翻译成另一种语言
- 转接头,让不同的插头和插座能够连接
核心口诀:
接口不兼容,
适配器来帮忙。
旧瓶装新酒,
老树开新花!
就像现实中的:
- 🔌 电源转接头:让不同国家的电器都能用
- 💬 翻译软件:让不同语言的人能交流
- 🔄 数据线转换头:让不同接口的设备能连接
- 🎮 游戏手柄转换器:让新手柄能在老主机上用
记住:适配器是补救措施,不是首选方案!在系统设计时应该尽量避免需要适配器的情况。
