【设计模式】适配器模式大白话讲解!

适配器模式大白话讲解

一句话概括

就像转接头,让不兼容的东西能够一起工作


现实生活比喻

场景1:电源转接头

  • 问题:中国插头(扁的)插不进欧洲插座(圆的)
  • 解决方案:用一个转接头,中国插头 → 转接头 → 欧洲插座
  • 结果:完美使用!

场景2:翻译官

  • 问题:中国人说中文,美国人说英文,互相听不懂
  • 解决方案:请个翻译,中文 → 翻译 → 英文
  • 结果:顺利沟通!

完整代码示例

场景:让旧式播放器支持新格式

java 复制代码
/**
 * 适配器模式 - 媒体播放器示例
 */
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 {
    @Override
    public 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 {
    @Override
    public void playVlc(String fileName) {
        System.out.println("播放VLC文件: " + fileName);
    }
    
    @Override
    public void playMp4(String fileName) {
        System.out.println("播放MP4文件: " + fileName);
    }
    
    @Override
    public void playAvi(String fileName) {
        System.out.println("播放AVI文件: " + fileName);
    }
}

/**
 * 适配器 - 核心!让老播放器支持新格式
 */
class MediaAdapter implements MediaPlayer {
    private AdvancedMediaPlayer advancedPlayer;
    
    public MediaAdapter() {
        this.advancedPlayer = new AdvancedPlayer();
    }
    
    @Override
    public 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,或者委托给MP3Player
            System.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

更真实的例子:数据库连接适配器

java 复制代码
/**
 * 数据库连接适配器示例
 */
public class DatabaseExample {
    public static void main(String[] args) {
        // 老系统用的MySQL
        Database 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 {
    @Override
    public void connect() {
        System.out.println("MySQL连接成功");
    }
    
    @Override
    public 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();
    }
    
    @Override
    public void connect() {
        mongoDB.mongoConnect();  // 适配:connect → mongoConnect
    }
    
    @Override
    public 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 复制代码
// 通过继承来实现适配(Java中不常用,因为单继承限制)
class ClassAdapter extends AdvancedPlayer implements MediaPlayer {
    @Override
    public void play(String fileName) {
        if (fileName.endsWith(".mp4")) {
            playMp4(fileName);  // 直接调用父类方法
        }
        // ... 其他格式
    }
}

2. 对象适配器(通过组合,推荐)

java 复制代码
// 通过组合来实现适配(更灵活)
class ObjectAdapter implements MediaPlayer {
    private AdvancedMediaPlayer advancedPlayer;  // 组合
    
    public ObjectAdapter() {
        this.advancedPlayer = new AdvancedPlayer();
    }
    
    @Override
    public void play(String fileName) {
        // 委托给advancedPlayer
        if (fileName.endsWith(".mp4")) {
            advancedPlayer.playMp4(fileName);
        }
    }
}

3. 接口适配器(缺省适配器)

java 复制代码
// 当接口方法太多时,提供默认空实现
abstract class MediaAdapter implements MediaPlayer {
    public void play(String fileName) {}  // 默认空实现
    public void stop() {}                 // 默认空实现
    public void pause() {}                // 默认空实现
}

// 使用时只需要重写需要的方法
class SimplePlayer extends MediaAdapter {
    @Override
    public void play(String fileName) {
        System.out.println("简单播放: " + fileName);
    }
    // 不需要实现stop和pause方法
}

适用场景(大白话版)

适合用适配器的场景:

  1. 系统升级

    java 复制代码
    // 老系统要接入新组件
    OldSystem system = new OldSystem();
    NewComponent adapter = new Adapter(newComponent);
    system.use(adapter);  // 老系统无感知
  2. 第三方库集成

    java 复制代码
    // 第三方库接口不匹配
    ThirdPartyLib lib = new ThirdPartyLib();
    OurInterface adapter = new LibAdapter(lib);
    ourSystem.use(adapter);
  3. 接口不兼容

    java 复制代码
    // 两个系统接口不同
    SystemA systemA = new SystemA();
    SystemB adapter = new ABAdapter(systemA);
    client.use(adapter);

不适合的场景:

  1. 可以直接修改源码的情况
  2. 系统设计阶段,应该直接设计统一接口
  3. 过度设计,如果只是临时用一次,不如直接写兼容代码

优缺点

优点:

  • 解耦:客户端和目标类解耦
  • 复用:可以复用现有的类
  • 灵活:可以适配多个不同的类

缺点:

  • 复杂度:增加了系统的类和对象数量
  • 性能:多了一层调用,有轻微性能损失

总结

适配器模式就是:

  • 中间人,在两个不兼容的接口之间搭桥
  • 翻译官,把一种语言翻译成另一种语言
  • 转接头,让不同的插头和插座能够连接

核心口诀:

接口不兼容,

适配器来帮忙。

旧瓶装新酒,

老树开新花!

就像现实中的:

  • 🔌 电源转接头:让不同国家的电器都能用
  • 💬 翻译软件:让不同语言的人能交流
  • 🔄 数据线转换头:让不同接口的设备能连接
  • 🎮 游戏手柄转换器:让新手柄能在老主机上用

记住:适配器是补救措施,不是首选方案!在系统设计时应该尽量避免需要适配器的情况。

相关推荐
金色熊族12 小时前
装饰器模式(c++版)
开发语言·c++·设计模式·装饰器模式
西红柿维生素14 小时前
23种设计模式-框架中的使用
java·开发语言·设计模式
数据知道15 小时前
Go语言设计模式:桥接模式详解
设计模式·golang·桥接模式
数据知道19 小时前
Go语言设计模式:原型模式详解
设计模式·golang·原型模式
豆苗学前端20 小时前
写给女朋友的第一封信,测试方法概论
前端·后端·设计模式
爱吃烤鸡翅的酸菜鱼21 小时前
如何掌握【Java】 IO/NIO设计模式?工厂/适配器/装饰器/观察者模式全解析
java·开发语言·后端·设计模式·nio
你的人类朋友1 天前
设计模式的原则有哪些?
前端·后端·设计模式
YA3331 天前
java设计模式八、组合模式
java·设计模式·组合模式
Yeniden2 天前
设计模式>原型模式大白话讲解:就像复印机,拿个原件一复印,就得到一模一样的新东西
java·设计模式·原型模式·1024程序员节