适配器模式
大约 6 分钟
适配器模式
什么是适配器模式
适配器模式(Adapter Pattern)是一种结构型设计模式,它允许不兼容的接口协同工作。适配器模式将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
适配器模式的核心思想是:
- 将一个接口转换成另一个接口
- 使得原本不兼容的类可以协同工作
- 保持现有类的复用性
为什么需要适配器模式
在软件开发中,我们经常会遇到以下情况:
- 使用第三方库或遗留代码,其接口与当前系统不兼容
- 需要复用现有类,但其接口不符合当前需求
- 系统需要集成多个不同接口的组件
适配器模式通过创建一个适配器类来解决这些问题,使得不兼容的接口能够协同工作。
适配器模式的结构
适配器模式包含以下几个角色:
- 目标接口(Target):客户期望的接口
- 适配者(Adaptee):需要适配的类
- 适配器(Adapter):通过包装适配者,将其接口转换成目标接口
- 客户端(Client):使用目标接口的类
适配器模式的实现
类适配器模式
// 目标接口
interface Target {
void request();
}
// 适配者类
class Adaptee {
public void specificRequest() {
System.out.println("适配者的方法被调用");
}
}
// 类适配器
class ClassAdapter extends Adaptee implements Target {
@Override
public void request() {
// 调用适配者的方法
specificRequest();
}
}
// 客户端代码
public class ClassAdapterDemo {
public static void main(String[] args) {
Target target = new ClassAdapter();
target.request();
}
}
对象适配器模式
// 对象适配器
class ObjectAdapter implements Target {
private Adaptee adaptee;
public ObjectAdapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
// 调用适配者的方法
adaptee.specificRequest();
}
}
// 客户端代码
public class ObjectAdapterDemo {
public static void main(String[] args) {
Adaptee adaptee = new Adaptee();
Target target = new ObjectAdapter(adaptee);
target.request();
}
}
实际应用示例
// 目标接口:音频播放器
interface MediaPlayer {
void play(String audioType, String fileName);
}
// 高级媒体播放器接口
interface AdvancedMediaPlayer {
void playVlc(String fileName);
void playMp4(String fileName);
}
// VLC播放器实现
class VlcPlayer implements AdvancedMediaPlayer {
@Override
public void playVlc(String fileName) {
System.out.println("播放VLC文件: " + fileName);
}
@Override
public void playMp4(String fileName) {
// 空实现
}
}
// MP4播放器实现
class Mp4Player implements AdvancedMediaPlayer {
@Override
public void playVlc(String fileName) {
// 空实现
}
@Override
public void playMp4(String fileName) {
System.out.println("播放MP4文件: " + fileName);
}
}
// 适配器类
class MediaAdapter implements MediaPlayer {
AdvancedMediaPlayer advancedMediaPlayer;
public MediaAdapter(String audioType) {
if ("vlc".equalsIgnoreCase(audioType)) {
advancedMediaPlayer = new VlcPlayer();
} else if ("mp4".equalsIgnoreCase(audioType)) {
advancedMediaPlayer = new Mp4Player();
}
}
@Override
public void play(String audioType, String fileName) {
if ("vlc".equalsIgnoreCase(audioType)) {
advancedMediaPlayer.playVlc(fileName);
} else if ("mp4".equalsIgnoreCase(audioType)) {
advancedMediaPlayer.playMp4(fileName);
}
}
}
// 音频播放器实现
class AudioPlayer implements MediaPlayer {
MediaAdapter mediaAdapter;
@Override
public void play(String audioType, String fileName) {
// 播放MP3文件
if ("mp3".equalsIgnoreCase(audioType)) {
System.out.println("播放MP3文件: " + fileName);
}
// 处理其他文件类型
else if ("vlc".equalsIgnoreCase(audioType) || "mp4".equalsIgnoreCase(audioType)) {
mediaAdapter = new MediaAdapter(audioType);
mediaAdapter.play(audioType, fileName);
} else {
System.out.println("不支持的音频格式: " + audioType);
}
}
}
// 使用示例
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");
}
}
适配器模式的应用场景
- 系统扩展:当希望复用一些现存的类,但其接口不符合当前需求时
- 第三方库集成:当需要使用第三方库,但其接口与当前系统不兼容时
- 遗留代码集成:当需要集成遗留代码,但其接口不符合当前设计时
- 接口统一:当需要统一多个不同接口的调用方式时
适配器模式的优缺点
优点
- 单一职责原则:可以将接口或数据转换代码从主要业务逻辑中分离
- 开闭原则:只要客户端代码通过目标接口与适配器进行交互,就能在不修改现有客户端代码的情况下添加新的适配器
- 复用性:可以复用现有的适配者类
- 透明性:客户端代码不需要了解适配者类的实现细节
缺点
- 类适配器的局限性:类适配器需要多重继承,在Java中不支持
- 代码复杂性:增加适配器类会增加代码的复杂性
- 性能开销:适配器模式可能会引入额外的性能开销
适配器模式与其他模式的比较
与桥接模式的区别
- 适配器模式:主要用于解决接口不兼容的问题
- 桥接模式:主要用于将抽象部分与实现部分分离,使它们可以独立变化
与装饰器模式的区别
- 适配器模式:改变接口以匹配客户端需求
- 装饰器模式:不改变接口,但增加对象的责任
与外观模式的区别
- 适配器模式:将一个接口转换成另一个接口
- 外观模式:为复杂的子系统提供一个简化的接口
适配器模式的变体
1. 双向适配器
// 双向适配器示例
class BidirectionalAdapter implements Target, Adaptee {
private Target target;
private Adaptee adaptee;
public BidirectionalAdapter(Target target, Adaptee adaptee) {
this.target = target;
this.adaptee = adaptee;
}
// 实现Target接口
@Override
public void request() {
adaptee.specificRequest();
}
// 实现Adaptee接口
@Override
public void specificRequest() {
target.request();
}
}
2. 默认适配器
// 默认适配器模式
abstract class DefaultAdapter implements AdvancedMediaPlayer {
// 为所有方法提供默认实现
@Override
public void playVlc(String fileName) {
// 默认空实现
}
@Override
public void playMp4(String fileName) {
// 默认空实现
}
}
// 具体实现类只需重写需要的方法
class SimpleVlcPlayer extends DefaultAdapter {
@Override
public void playVlc(String fileName) {
System.out.println("播放VLC文件: " + fileName);
}
}
实际项目中的应用
// 数据库适配器示例
// 目标接口
interface DatabaseConnection {
void connect();
void executeQuery(String sql);
void close();
}
// MySQL适配者
class MySQLConnection {
public void mysqlConnect() {
System.out.println("连接MySQL数据库");
}
public void mysqlExecute(String sql) {
System.out.println("MySQL执行SQL: " + sql);
}
public void mysqlClose() {
System.out.println("关闭MySQL连接");
}
}
// PostgreSQL适配者
class PostgreSQLConnection {
public void pgConnect() {
System.out.println("连接PostgreSQL数据库");
}
public void pgExecute(String sql) {
System.out.println("PostgreSQL执行SQL: " + sql);
}
public void pgClose() {
System.out.println("关闭PostgreSQL连接");
}
}
// MySQL适配器
class MySQLAdapter implements DatabaseConnection {
private MySQLConnection mysqlConnection;
public MySQLAdapter() {
this.mysqlConnection = new MySQLConnection();
}
@Override
public void connect() {
mysqlConnection.mysqlConnect();
}
@Override
public void executeQuery(String sql) {
mysqlConnection.mysqlExecute(sql);
}
@Override
public void close() {
mysqlConnection.mysqlClose();
}
}
// PostgreSQL适配器
class PostgreSQLAdapter implements DatabaseConnection {
private PostgreSQLConnection pgConnection;
public PostgreSQLAdapter() {
this.pgConnection = new PostgreSQLConnection();
}
@Override
public void connect() {
pgConnection.pgConnect();
}
@Override
public void executeQuery(String sql) {
pgConnection.pgExecute(sql);
}
@Override
public void close() {
pgConnection.pgClose();
}
}
// 数据库工厂
class DatabaseFactory {
public static DatabaseConnection getConnection(String type) {
if ("mysql".equalsIgnoreCase(type)) {
return new MySQLAdapter();
} else if ("postgresql".equalsIgnoreCase(type)) {
return new PostgreSQLAdapter();
}
return null;
}
}
// 使用示例
public class DatabaseAdapterDemo {
public static void main(String[] args) {
// 使用MySQL
DatabaseConnection mysqlConn = DatabaseFactory.getConnection("mysql");
mysqlConn.connect();
mysqlConn.executeQuery("SELECT * FROM users");
mysqlConn.close();
System.out.println();
// 使用PostgreSQL
DatabaseConnection pgConn = DatabaseFactory.getConnection("postgresql");
pgConn.connect();
pgConn.executeQuery("SELECT * FROM users");
pgConn.close();
}
}
总结
适配器模式是一种非常实用的结构型设计模式,它通过创建适配器类来解决接口不兼容的问题。在实际开发中,当我们需要集成第三方库、复用现有代码或统一不同接口时,适配器模式是一个很好的选择。
使用适配器模式的关键点:
- 识别出接口不兼容的类或组件
- 定义目标接口,明确客户端的需求
- 创建适配器类,将适配者的接口转换为目标接口
- 客户端通过目标接口使用适配器,而不需要了解适配者的具体实现
适配器模式的优点是可以复用现有代码,保持系统的开放性和可扩展性,但也需要注意避免过度使用,以免增加系统的复杂性。在现代Java开发中,适配器模式常用于框架集成、API适配、遗留系统集成等场景。