目录
模式动机
在软件开发过程中,经常会遇到需要使用已有类的功能,但这些类的接口不符合当前系统的需求,或者需要将不同的类的接口统一起来的情况。适配器模式正是为了解决这类问题而设计的。通过适配器模式,可以将一个类的接口转换成另一个接口,从而使原本不兼容的类可以一起工作。
模式定义
适配器(Adapter)模式是一种结构型设计模式,它允许将一个类的接口转换成客户端期望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的类可以一起工作。
模式结构
适配器模式包含以下几个主要角色:
- Target(目标接口):定义客户端所期待的接口。
- Adaptee(适配者):已经存在的类,需要适配的类,它提供了一些有用的方法,但接口不符合客户端的要求。
- Adapter(适配器):适配器类,它实现了目标接口,并将请求转发给适配者类。
类适配器
类适配器通过继承适配者类来实现目标接口。
对象适配器
对象适配器通过在适配器类中包含适配者对象的实例来实现目标接口。
代码分析
类适配器示例
// 目标接口
interface Target {
void request();
}
// 适配者类
class Adaptee {
public void specificRequest() {
System.out.println("Adaptee specificRequest");
}
}
// 类适配器
class ClassAdapter extends Adaptee implements Target {
@Override
public void request() {
specificRequest();
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Target target = new ClassAdapter();
target.request();
}
}
对象适配器示例
// 目标接口
interface Target {
void request();
}
// 适配者类
class Adaptee {
public void specificRequest() {
System.out.println("Adaptee specificRequest");
}
}
// 对象适配器
class ObjectAdapter implements Target {
private Adaptee adaptee;
public ObjectAdapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
adaptee.specificRequest();
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Adaptee adaptee = new Adaptee();
Target target = new ObjectAdapter(adaptee);
target.request();
}
}
模式分析
适配器模式的核心思想是通过适配器类来转换接口,使得原本不兼容的类可以一起工作。适配器模式有两种实现方式:类适配器和对象适配器。类适配器通过继承适配者类来实现目标接口,而对象适配器通过在适配器类中包含适配者对象的实例来实现目标接口。
实例
假设有一个旧的音频播放器类,只能播放 MP3 格式的音频文件。现在需要扩展该播放器,使其能够播放新的音频格式,如 VLC 和 MP4。可以通过适配器模式来实现这一需求。
// 目标接口
interface MediaPlayer {
void play(String audioType, String fileName);
}
// 适配者类:VLC 播放器
class VlcPlayer {
public void playVlc(String fileName) {
System.out.println("Playing vlc file. Name: " + fileName);
}
}
// 适配者类:MP4 播放器
class Mp4Player {
public void playMp4(String fileName) {
System.out.println("Playing mp4 file. Name: " + fileName);
}
}
// 对象适配器:音频播放器适配器
class MediaAdapter implements MediaPlayer {
private MediaPlayer advancedMediaPlayer;
public MediaAdapter(String audioType) {
if (audioType.equalsIgnoreCase("vlc")) {
advancedMediaPlayer = new VlcPlayer();
} else if (audioType.equalsIgnoreCase("mp4")) {
advancedMediaPlayer = new Mp4Player();
}
}
@Override
public void play(String audioType, String fileName) {
if (audioType.equalsIgnoreCase("vlc")) {
((VlcPlayer) advancedMediaPlayer).playVlc(fileName);
} else if (audioType.equalsIgnoreCase("mp4")) {
((Mp4Player) advancedMediaPlayer).playMp4(fileName);
}
}
}
// 原始音频播放器
class AudioPlayer implements MediaPlayer {
@Override
public void play(String audioType, String fileName) {
if (audioType.equalsIgnoreCase("mp3")) {
System.out.println("Playing mp3 file. Name: " + fileName);
} else if (audioType.equalsIgnoreCase("vlc") || audioType.equalsIgnoreCase("mp4")) {
MediaAdapter mediaAdapter = new MediaAdapter(audioType);
mediaAdapter.play(audioType, fileName);
} else {
System.out.println("Invalid media. " + audioType + " format not supported");
}
}
}
// 客户端
public class Client {
public static void main(String[] args) {
AudioPlayer audioPlayer = new AudioPlayer();
audioPlayer.play("mp3", "beyond.mp3");
audioPlayer.play("mp4", "alone.mp4");
audioPlayer.play("vlc", "far far away.vlc");
audioPlayer.play("avi", "mind.mind");
}
}
优点
- 提高类的透明性和复用性:通过适配器模式,可以将已有的类或接口适配成新的接口,提高类的透明性和复用性。
- 灵活性:适配器模式使得系统更容易扩展,可以在不修改原有代码的情况下引入新的适配器类。
- 开闭原则:符合开闭原则,可以在不修改现有代码的情况下引入新的适配器类。
缺点
- 增加系统复杂性:适配器模式会增加系统的复杂性,因为需要引入额外的适配器类。
- 类的膨胀:如果需要适配的类很多,可能会导致适配器类的数量增加,从而增加系统的维护成本。
适用环境
- 系统需要使用现有的类,但其接口不符合系统的需求。
- 希望增加系统的灵活性和可扩展性。
- 需要将不同的类的接口统一起来。
模式应用
适配器模式在实际开发中应用广泛,特别是在以下场景中:
- 遗留系统集成:将旧的系统或类集成到新的系统中。
- 框架扩展:在不修改框架源码的情况下,通过适配器模式扩展框架的功能。
- 第三方库集成:将第三方库的接口适配成系统需要的接口。
模式扩展
适配器模式可以与其他设计模式结合使用,例如:
- 工厂模式:可以使用工厂模式来创建适配器对象,从而进一步提高系统的灵活性。
- 装饰器模式:可以在适配器的基础上使用装饰器模式,进一步增强适配后的对象的功能。
总结
适配器模式通过适配器类将一个类的接口转换成另一个接口,使得原本不兼容的类可以一起工作。适配器模式分为类适配器和对象适配器两种实现方式。适配器模式提高了类的透明性和复用性,增加了系统的灵活性和可扩展性,但在某些情况下会增加系统的复杂性和维护成本。适配器模式适用于需要将不同类的接口统一起来的场景,广泛应用于系统集成、框架扩展和第三方库集成等场景中。