适配器模式 (Adapter Pattern)
将一个类的接口转换成客户希望的另一个接口
模式定义
适配器模式是一种结构型设计模式,它允许不兼容的接口协同工作。适配器模式将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的类可以一起工作。
模式结构
graph TD
A[Client] --> B((Target))
B --> C[Adapter]
C --> D[Adaptee]
实现方式
类适配器
通过继承实现适配器,适配器类同时继承目标接口和被适配者类。
// 目标接口
public interface MediaPlayer {
void play(String audioType, String fileName);
}
// 高级媒体播放器接口
public interface AdvancedMediaPlayer {
void playVlc(String fileName);
void playMp4(String fileName);
}
// 具体的高级媒体播放器实现
public class VlcPlayer implements AdvancedMediaPlayer {
@Override
public void playVlc(String fileName) {
System.out.println("Playing vlc file. Name: " + fileName);
}
@Override
public void playMp4(String fileName) {
// 什么都不做
}
}
public class Mp4Player implements AdvancedMediaPlayer {
@Override
public void playVlc(String fileName) {
// 什么都不做
}
@Override
public void playMp4(String fileName) {
System.out.println("Playing mp4 file. Name: " + fileName);
}
}
// 类适配器 - 继承被适配者并实现目标接口
public class MediaAdapter extends VlcPlayer implements MediaPlayer {
AdvancedMediaPlayer advancedMusicPlayer;
public MediaAdapter(String audioType) {
if (audioType.equalsIgnoreCase("vlc")) {
advancedMusicPlayer = new VlcPlayer();
} else if (audioType.equalsIgnoreCase("mp4")) {
advancedMusicPlayer = new Mp4Player();
}
}
@Override
public void play(String audioType, String fileName) {
if (audioType.equalsIgnoreCase("vlc")) {
advancedMusicPlayer.playVlc(fileName);
} else if (audioType.equalsIgnoreCase("mp4")) {
advancedMusicPlayer.playMp4(fileName);
}
}
}
对象适配器
通过组合实现适配器,适配器类持有被适配者对象的引用。
// 对象适配器 - 使用组合而非继承
public class MediaAdapterObject implements MediaPlayer {
AdvancedMediaPlayer advancedMusicPlayer;
public MediaAdapterObject(String audioType) {
if (audioType.equalsIgnoreCase("vlc")) {
advancedMusicPlayer = new VlcPlayer();
} else if (audioType.equalsIgnoreCase("mp4")) {
advancedMusicPlayer = new Mp4Player();
}
}
@Override
public void play(String audioType, String fileName) {
if (audioType.equalsIgnoreCase("vlc")) {
advancedMusicPlayer.playVlc(fileName);
} else if (audioType.equalsIgnoreCase("mp4")) {
advancedMusicPlayer.playMp4(fileName);
}
}
}
// 音频播放器类
public class AudioPlayer implements MediaPlayer {
MediaAdapterObject mediaAdapter;
@Override
public void play(String audioType, String fileName) {
// 播放mp3音乐文件的内置支持
if (audioType.equalsIgnoreCase("mp3")) {
System.out.println("Playing mp3 file. Name: " + fileName);
}
// mediaAdapter提供了播放其他文件格式的支持
else if (audioType.equalsIgnoreCase("vlc") || audioType.equalsIgnoreCase("mp4")) {
mediaAdapter = new MediaAdapterObject(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");
}
}
经典案例
Java I/O 流
Java I/O 流库中大量使用了适配器模式。
// InputStreamReader 是适配器,将字节流适配为字符流
public class InputStreamReaderExample {
public static void main(String[] args) throws Exception {
// FileInputStream 是被适配者(字节流)
FileInputStream fis = new FileInputStream("example.txt");
// InputStreamReader 是适配器,将字节流适配为字符流
InputStreamReader isr = new InputStreamReader(fis);
// 现在可以像使用字符流一样使用它
int data = isr.read();
while (data != -1) {
System.out.print((char) data);
data = isr.read();
}
isr.close();
}
}
// OutputStreamWriter 是适配器,将字符流适配为字节流
public class OutputStreamWriterExample {
public static void main(String[] args) throws Exception {
// FileOutputStream 是被适配者(字节流)
FileOutputStream fos = new FileOutputStream("output.txt");
// OutputStreamWriter 是适配器,将字符流适配为字节流
OutputStreamWriter osw = new OutputStreamWriter(fos);
// 现在可以像使用字符流一样使用它
osw.write("Hello, 适配器模式!");
osw.close();
}
}
Spring框架中的适配器
Spring MVC 中的 HandlerAdapter 是适配器模式的典型应用。
// Spring MVC 中的 HandlerAdapter 接口
public interface HandlerAdapter {
boolean supports(Object handler);
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
long getLastModified(HttpServletRequest request, Object handler);
}
// 具体的适配器实现
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return (handler instanceof Controller);
}
@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return ((Controller) handler).handleRequest(request, response);
}
@Override
public long getLastModified(HttpServletRequest request, Object handler) {
if (handler instanceof LastModified) {
return ((LastModified) handler).getLastModified(request);
}
return -1L;
}
}
// 另一个适配器实现
public class HttpRequestHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return (handler instanceof HttpRequestHandler);
}
@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
((HttpRequestHandler) handler).handleRequest(request, response);
return null;
}
@Override
public long getLastModified(HttpServletRequest request, Object handler) {
if (handler instanceof LastModified) {
return ((LastModified) handler).getLastModified(request);
}
return -1L;
}
}
适用场景
- 系统需要使用现有的类,但其接口不符合需求
- 想要建立一个可以重复使用的类
- 需要在多个现有类之间进行适配
- 当希望复用一些现存的类,但接口又与复用环境要求不一致时
优缺点
优点
- 提高复用性:适配器模式可以让任何两个没有关联的类一起运行,提高了类的复用
- 增加类的透明度:适配器模式将目标类和适配者类解耦
- 灵活性好:一个适配者类或目标类本身都不能胜任的工作,通过适配器模式可以完成
缺点
- 过多使用适配器会让系统非常零乱,不容易整体进行把握
- 类适配器的限制:需要继承适配者类,这要求适配者类不能为final类
- 增加系统复杂度:适配器模式会增加代码的复杂度,理解起来更困难