模式定义

适配器模式是一种结构型设计模式,它允许不兼容的接口协同工作。适配器模式将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的类可以一起工作。

模式结构

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类
  • 增加系统复杂度:适配器模式会增加代码的复杂度,理解起来更困难