是设计模式,我们有救了!!!(五、适配器模式)

48 阅读4分钟

代码地址github.com/YaYiXiBa/Ja…

适配器模式是一种结构型设计模式,它允许不兼容的接口之间进行协作,就像现实世界中的电源适配器一样,让不同规格的插头能够正常工作。

应用场景其实还挺多见的,比如目前你负责的一个接口,本来是只能解析JSon文件的,但是后来对接新的公司,他给你一份Excel的文件,同样还要用这个接口解析。原本两种接口是不兼容的,但是你在原接口的实现类里面注入一个适配器,而这个适配器是可以处理新类型的文件的,这样就让两个不兼容的接口,一起工作了。

优点与缺点

  • 优点

    • 解决接口不兼容问题,提高类的复用性。
    • 降低客户端与适配者的耦合度,符合 “开闭原则”(无需修改原有代码)。
  • 缺点

    • 增加系统复杂度,引入额外的适配器类。
    • 过多使用适配器可能导致代码逻辑混乱,不易维护。

举例:现在有这样一个要求,你原来有一个MP3(实现类),具有播放(抽象方法).mp3格式的媒体文件的功能(接口)。代码如下,很明显,如果是其它格式的媒体文件,就会报错。适配器的思想就是,让这个Mp3具有播放其他文件的能力。我们这里的播放:System.out.println("Playing mp3: "+file);写的可能过于简单了,实际上可能代码会很多,因此直接通过if else来修改会变更大量的代码。

public class Mp3Player implements MediaPlayer{

    @Override
    public void play(String type, String file) {
        if(type.equals("mp3") && file.endsWith(".mp3")){
            System.out.println("Playing mp3: "+file);
        } else{
            System.out.println("Not supported");
        }
    }
}

适配器模式做法:

  1. 先提出一个接口,该接口要求对于可能扩展的媒体文件,每种格式都有单一的处理,这样可以方便后期再次添加文件格式。
public interface AdvancedMediaPlayer {
    public void playVlc(String fileName);
    public void playMp4(String fileName);
}

2. 接口有了,可以构建几个实现类。Mp4Player专注于playMp4方法就好。Vlc同理

public class Mp4Player implements AdvancedMediaPlayer {

    @Override
    public void playVlc(String fileName) {
        System.out.println("Not Supported");
    }

    @Override
    public void playMp4(String fileName) {
        System.out.println("Playing MP4 file: " + fileName);
    }
}
public class VlcPlayer implements AdvancedMediaPlayer {
    @Override
    public void playVlc(String fileName) {
        System.out.println("Playing VLC file: " + fileName);
    }

    @Override
    public void playMp4(String fileName) {
        System.out.println("Not Supported");
    }
}

3. 接下来直接把两个实现类注入MP3Player??不,这就不是适配了。接下来要用到的是聚合的思想。我们真正要注入给MP3Player的是MediaAdapter,它就像是一个转接口,当MP3Player播放不了的时候,直接交给它就行。

//既然适配器要接手原来MP3处理不了的工作,那去先去实现相关的接口
public class MediaAdapter implements MediaPlayer {
    //对于额外的媒体已经定义了一个统一的接口,那么直接在此处定义接口成员变量,因为具体用的是哪个子类,要看MP3遇到的是哪一种类型。
    private final AdvancedMediaPlayer advancedMediaPlayer;
    //根据媒体类型决定到底用那个子类
    public MediaAdapter(String mediaType) {
        if (mediaType.equalsIgnoreCase("mp4")) {
            advancedMediaPlayer = new Mp4Player();
        }else if (mediaType.equalsIgnoreCase("vlc")) {
            advancedMediaPlayer = new VlcPlayer();
        }else{
            throw new IllegalArgumentException("Unsupported Media Type");
        }
    }

    //每种子类的方法是不一样滴
    @Override
    public void play(String type, String file) {
        if (type.equals("mp4")) {
            advancedMediaPlayer.playMp4(file);
        } else if (type.equals("vlc")) {
            advancedMediaPlayer.playVlc(file);
        }else{
            System.out.println("Unsupported Media Type");
        }
    }
}

4.最后,把我们的MP3改成下面这样:

在MP3中不需要改动原来的处理逻辑(在这个例子中指的就是: System.out.println("Playing mp3: "+file);)那么如果碰到处理不了的,直接交给mediaAdapter,由于mediaAdapter实现了play方法,MP3就不需要管适配器用的是哪一个子类的具体方法,直接play()。适配器内部再根据mp3player的调用来实例化内部的成员,调用相关的方法。后续如果要再加新的媒体格式,只要在创建一个AdvancedMediaPlayer的子类,而在MP3Player中,仍无需大量改动,只加一个判断条件就可以。

public class Mp3Player implements MediaPlayer{
    MediaAdapter mediaAdapter;

    @Override
    public void play(String type, String file) {
        if(type.equals("mp3") && file.endsWith(".mp3")){
            System.out.println("Playing mp3: "+file);
        } else if (type.equals("mp4") || type.equals("vlc")) {
            mediaAdapter = new MediaAdapter(type);
            mediaAdapter.play(type, file);
        } else{
            System.out.println("Not supported");
        }
    }
}

类图放在最后: image.png

代码地址github.com/YaYiXiBa/Ja…