什么是适配器模式?

610 阅读5分钟

1. 什么是适配器模式?

适配器模式(Adapter Pattern)是一种结构型设计模式,它的主要作用是在不修改现有代码的情况下,使得原本不兼容的接口能够相互协作。这个模式通常用于实现新旧系统的兼容、整合不同接口的组件,以及在不改变现有组件的前提下增强其功能。

适配器模式的关键组件包括以下几部分:

  1. 目标接口(Target Interface):这是客户端期望使用的接口,适配器需要实现这个接口,以便客户端可以直接使用适配器。
  2. 待适配的类(Adaptee):这是一个已有的类或系统,其接口与目标接口不兼容,需要通过适配器进行转换。
  3. 适配器类(Adapter):这是适配器模式的核心组件。适配器类实现了目标接口,并持有待适配类的实例。适配器类的方法通常会调用待适配类的方法,将其输出转换为目标接口所需要的格式。

适配器模式可以分为两种实现方式:类适配器(Class Adapter)和对象适配器(Object Adapter)。类适配器是通过继承的方式实现适配,适配器类继承待适配类并实现目标接口。而对象适配器则是通过组合的方式实现适配,适配器类持有待适配类的实例并实现目标接口。

总的来说,适配器模式是一种灵活的设计模式,可以帮助我们在保持现有代码稳定的同时,实现新旧系统的整合和功能扩展。

2. 对象适配器模式的实现

假设我们有一个MediaPlayer接口,它有一个play方法,我们希望实现这个接口的类可以播放不同类型的媒体文件。现在我们有一个AdvancedMediaPlayer接口,它支持播放MP4和FLV格式的文件。我们希望通过适配器模式使MediaPlayer接口能够使用AdvancedMediaPlayer的功能。

// 目标接口
interface MediaPlayer {
    void play(String audioType, String fileName);
}

// 待适配的类接口
interface AdvancedMediaPlayer {
    void playMp4(String fileName);
    void playFlv(String fileName);
}

// 待适配的类实现
class Mp4Player implements AdvancedMediaPlayer {
    @Override
    public void playMp4(String fileName) {
        System.out.println("Playing MP4 file: " + fileName);
    }

    @Override
    public void playFlv(String fileName) {
        // Do nothing
    }
}

class FlvPlayer implements AdvancedMediaPlayer {
    @Override
    public void playMp4(String fileName) {
        // Do nothing
    }

    @Override
    public void playFlv(String fileName) {
        System.out.println("Playing FLV file: " + fileName);
    }
}

// 适配器类
class MediaAdapter implements MediaPlayer {
    private AdvancedMediaPlayer advancedMediaPlayer;

    public MediaAdapter(String audioType) {
        if (audioType.equalsIgnoreCase("mp4")) {
            advancedMediaPlayer = new Mp4Player();
        } else if (audioType.equalsIgnoreCase("flv")) {
            advancedMediaPlayer = new FlvPlayer();
        }
    }

    @Override
    public void play(String audioType, String fileName) {
        if (audioType.equalsIgnoreCase("mp4")) {
            advancedMediaPlayer.playMp4(fileName);
        } else if (audioType.equalsIgnoreCase("flv")) {
            advancedMediaPlayer.playFlv(fileName);
        }
    }
}

// 客户端类
class AudioPlayer implements MediaPlayer {
    @Override
    public void play(String audioType, String fileName) {
        if (audioType.equalsIgnoreCase("mp3")) {
            System.out.println("Playing MP3 file: " + fileName);
        } else if (audioType.equalsIgnoreCase("mp4") || audioType.equalsIgnoreCase("flv")) {
            MediaAdapter mediaAdapter = new MediaAdapter(audioType);
            mediaAdapter.play(audioType, fileName);
        } else {
            System.out.println("Invalid media format: " + audioType);
        }
    }
}

public class AdapterPatternDemo {
    public static void main(String[] args) {
        AudioPlayer audioPlayer = new AudioPlayer();

        audioPlayer.play("mp3", "song.mp3");
        audioPlayer.play("mp4", "video.mp4");
        audioPlayer.play("flv", "video.flv");
        audioPlayer.play("avi", "video.avi");
    }
}

在这个例子中,MediaPlayer是目标接口,AdvancedMediaPlayer是待适配的接口。我们创建了一个MediaAdapter类来实现MediaPlayer接口,它包含一个AdvancedMediaPlayer实例。AudioPlayer是客户端类,它实现了MediaPlayer接口,当客户端需要播放MP4或FLV格式的文件时,会调用MediaAdapterplay方法。 通过这个例我们可以看到适配器模式是如何在不修改原有接口的情况下,实现新旧系统的整合和功能扩展的。在这个例子中,我们实现了一个AudioPlayer类,它可以播放多种媒体格式,包括MP3、MP4和FLV。当需要播放MP4或FLV格式的文件时,AudioPlayer会使用MediaAdapter适配器来调用AdvancedMediaPlayer接口的实现。

在下面补充一下对象适配器模式和类适配器模式的区别: 对象适配器模式通过组合(持有待适配类的实例)来实现适配,而类适配器模式通过继承(适配器类继承待适配类)来实现适配。

以下是一个简单的Java代码示例,展示了对象适配器模式的实现:

interface Target {
    void request();
}

class Adaptee {
    void specificRequest() {
        System.out.println("Called specificRequest()");
    }
}

class ObjectAdapter implements Target {
    private Adaptee adaptee;

    public ObjectAdapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }

    @Override
    public void request() {
        adaptee.specificRequest();
    }
}

public class Main {
    public static void main(String[] args) {
        Adaptee adaptee = new Adaptee();
        Target target = new ObjectAdapter(adaptee);
        target.request();
    }
}

以下是一个简单的Java代码示例,展示了类适配器模式的实现:

interface Target {
    void request();
}

class Adaptee {
    void specificRequest() {
        System.out.println("Called specificRequest()");
    }
}

class ClassAdapter extends Adaptee implements Target {
    @Override
    public void request() {
        specificRequest();
    }
}

public class Main {
    public static void main(String[] args) {
        Target target = new ClassAdapter();
        target.request();
    }
}

从这两个示例可以看出,对象适配器模式将待适配类的实例作为成员变量,通过构造函数传入。在适配器类的实现方法中,调用待适配类实例的方法。而类适配器模式将适配器类定义为待适配类的子类,这样适配器类可以直接调用待适配类的方法。

对象适配器模式相对于类适配器模式具有更好的灵活性和扩展性,因为它不依赖于继承关系,可以适配多个待适配类。而类适配器模式由于使用了继承,可能受到单继承限制,不过它更简单且易于理解。在实际项目中,根据具体需求和场景选择合适的适配器模式实现方式是至关重要的。

3. 总结

适配器模式并非适用于所有场景。在某些情况下,重构现有代码以实现接口一致性可能是更好的选择。适配器模式通常适用于以下场景:

  1. 当两个接口之间存在兼容性问题时,使用适配器模式可以实现它们的协作。
  2. 当需要整合多个不同接口的组件时,适配器模式可以提供一个统一的接口。
  3. 当需要在不改变现有组件的前提下扩展其功能时,适配器模式可以实现逐步扩展。

总之,适配器模式是一种有用的设计模式,可以帮助我们在保持现有代码稳定的同时,实现新旧系统的整合和功能扩展。在实际项目中,根据具体需求和场景选择合适的设计模式来解决问题是至关重要的。