适配器模式概述

news2025/1/4 18:33:57

大体介绍

适配器模式(Adapter Pattern)是一种结构型设计模式,其核心目的是通过提供一个适配器类来使得原本接口不兼容的类可以一起工作。它通过将一个类的接口转换成客户端所期望的接口,使得原本因接口不兼容而无法一起工作的类可以协同工作。

适配器模式的定义

适配器模式是一种结构型设计模式,允许将一个类的接口转换成客户希望的另一个接口,从而解决由于接口不兼容而导致的类无法协同工作的难题。

适配器模式的组成部分

  1. 目标接口(Target):客户端所期望的接口,可以是现有的接口,也可以是新定义的接口。
  2. 源接口(Adaptee):需要适配的现有接口,它和目标接口不兼容。
  3. 适配器(Adapter):负责将源接口转化为目标接口的类,它实现目标接口,并且在方法内部调用源接口的实现,从而使得客户端可以使用目标接口与源接口进行交互。

适配器模式的工作流程

  • 客户端调用目标接口的相关方法,而目标接口的实现由适配器来提供。
  • 适配器将客户端的请求转化成源接口可以理解的请求,完成适配过程。
  • 通过适配器,客户端无需改变代码,只需通过适配器与源接口协作即可。

适配器模式的类型

  1. 类适配器(Class Adapter):通过继承的方式实现目标接口和源接口的适配。适配器类继承了源接口的实现类,并实现目标接口。
  2. 对象适配器(Object Adapter):通过组合的方式实现目标接口和源接口的适配。适配器类包含源接口的实例,并通过代理调用源接口的方法来适配目标接口。

适配器模式的优缺点

优点:
  • 可以增加类的透明性:客户端可以通过目标接口与类交互,而无需了解适配器的存在,适配器隐藏了源接口的复杂性。
  • 增强系统的可扩展性:可以通过适配器模式对现有的类进行改造,使其具备新功能,而不需要修改源代码。
  • 支持多个不同的接口:适配器可以将多个不同的接口适配到一个目标接口,提升系统的灵活性。
缺点:
  • 增加代码复杂性:每个源接口都需要一个适配器类,这可能会导致系统中出现大量的适配器类,增加代码复杂性。
  • 可能会影响性能:每次调用方法时都需要通过适配器进行转发,可能会对系统性能产生一定的影响。

适配器模式的应用场景

适配器模式非常适用于以下几种场景:

  1. 需要使用现有类,但其接口不符合需求时:通过适配器将源类的接口转化为目标接口。
  2. 希望类可以和不兼容的接口一起工作时:可以通过适配器模式将不兼容的接口适配成兼容接口,避免直接修改类的代码。
  3. 在复用已有的类库时:有时第三方库的接口可能与现有系统接口不兼容,可以通过适配器模式使其兼容。
  4. 希望系统中的多个接口能够统一时:可以通过适配器模式将多个接口统一为一个目标接口,简化系统的调用。

适配器模式的例子

  1. MediaPlayer:目标接口,客户端通过它来播放不同类型的媒体(MP3,MP4,VLC)。
  2. AudioPlayer:适配者类,负责播放音频,在加入适配器之前只能播放MP3。
  3. MP4PlayerVLCPlayer:高级接口,分别支持播放 MP4 格式和 VLC 格式的媒体。
  4. MediaAdapter:适配器类,负责将不兼容的接口(如 MP4PlayerVLCPlayer)适配到 MediaPlayer 接口。

接下来,我们将逐步详细解析这个例子。

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

// 具体类:AudioPlayer
class AudioPlayer implements MediaPlayer {

    MediaAdapter mediaAdapter;

    @Override
    public void play(String audioType, String fileName) {

        // 播放MP3文件
        if(audioType.equalsIgnoreCase("mp3")){
            System.out.println("Playing mp3 file. Name: " + fileName);
        }
        // 对于其他文件类型,通过适配器来处理
        else if(audioType.equalsIgnoreCase("mp4") || audioType.equalsIgnoreCase("vlc")){
            mediaAdapter = new MediaAdapter(audioType);
            mediaAdapter.play(audioType, fileName);
        }
        else {
            System.out.println("Invalid media. " + audioType + " format not supported");
        }
    }
}

// 原接口:AdvancedMediaPlayer
interface AdvancedMediaPlayer {
    void playVlc(String fileName);
    void playMp4(String fileName);
}

// 具体类:VlcPlayer(实现AdvancedMediaPlayer)
class VlcPlayer implements AdvancedMediaPlayer {

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

    @Override
    public void playMp4(String fileName) {
        // 无法播放 MP4 文件
    }
}

// 具体类:Mp4Player(实现AdvancedMediaPlayer)
class Mp4Player implements AdvancedMediaPlayer {

    @Override
    public void playVlc(String fileName) {
        // 无法播放 VLC 文件
    }

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

// 适配器类:MediaAdapter
class MediaAdapter 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);
        }
    }
}

// 客户端代码:AdapterPatternDemo
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");
    }
}

代码结构分析

  1. 目标接口:MediaPlayer

    • 这是客户端所期望的接口,用于播放不同类型的媒体。客户端只通过这个接口来播放音频,不需要关心具体播放的是 MP3、MP4 还是 VLC 格式的文件。
    interface MediaPlayer {
        void play(String audioType, String fileName);
    }
    
  2. 具体类:AudioPlayer

    • AudioPlayer 是实现了 MediaPlayer 接口的类,支持播放 MP3 格式的音频文件。
    • 对于 MP4 和 VLC 格式的音频,AudioPlayer 无法直接播放。于是它会通过 MediaAdapter 来适配这些格式。
    class AudioPlayer implements MediaPlayer {
        MediaAdapter mediaAdapter;
    
        @Override
        public void play(String audioType, String fileName) {
            if(audioType.equalsIgnoreCase("mp3")){
                System.out.println("Playing mp3 file. Name: " + fileName);
            }
            else if(audioType.equalsIgnoreCase("mp4") || audioType.equalsIgnoreCase("vlc")){
                mediaAdapter = new MediaAdapter(audioType);
                mediaAdapter.play(audioType, fileName);
            }
            else {
                System.out.println("Invalid media. " + audioType + " format not supported");
            }
        }
    }
    

    这里,AudioPlayer 通过判断音频格式来决定是否使用适配器。它直接播放 MP3 文件,对于 MP4 和 VLC 文件则通过 MediaAdapter 进行适配。

  3. 原接口:AdvancedMediaPlayer

    • AdvancedMediaPlayer 是一个原接口,定义了播放 MP4 和 VLC 格式文件的方法。
    • 它有两个实现类:VlcPlayerMp4Player。这些类分别负责播放各自支持的格式,但它们的接口与 MediaPlayer 不兼容。
    interface AdvancedMediaPlayer {
        void playVlc(String fileName);
        void playMp4(String fileName);
    }
    

    VlcPlayer 负责播放 .vlc 文件,Mp4Player 负责播放 .mp4 文件。两个类的接口都与 MediaPlayer 不兼容,因此我们需要适配器来实现兼容。

  4. 适配器类:MediaAdapter

    • MediaAdapter 是核心的适配器类,它将不兼容的接口(AdvancedMediaPlayer)转换为客户端需要的接口(MediaPlayer)。
    • 它实现了 MediaPlayer 接口,并根据需要调用 VlcPlayerMp4Player 的相应方法。
class MediaAdapter 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) {
        advancedMusicPlayer.play(fileName);  // 直接调用通用的 play 方法
    }
}

  • MediaAdapter 根据传入的音频类型(vlcmp4)创建相应的 AdvancedMediaPlayer 对象。
  • 然后它会调用 VlcPlayerMp4PlayerplayVlc()playMp4() 方法,完成对 VLC 或 MP4 文件的播放。
  1. 客户端代码:AdapterPatternDemo

    • 客户端通过 AudioPlayer 来播放各种格式的音频文件。即使客户端只知道 MediaPlayer 接口,它依然可以播放 MP3、MP4 和 VLC 文件,因为 AudioPlayer 通过 MediaAdapter 实现了适配功能。
    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");
        }
    }
    

运行结果

Playing mp3 file. Name: beyond the horizon.mp3
Playing mp4 file. Name: alone.mp4
Playing vlc file. Name: far far away.vlc
Invalid media. avi format not supported

详细解释

  1. 目标接口 MediaPlayer:定义了 play 方法,所有的播放器类(AudioPlayer 和适配器)都实现了这个接口。

  2. AudioPlayer:实现了 MediaPlayer 接口,直接支持播放 MP3 文件。对于其他格式(mp4vlc),它创建 MediaAdapter 来适配这两种格式并播放相应的文件。

  3. MediaAdapter:适配器的核心作用是将 VlcPlayerMp4Player 的方法适配到 MediaPlayer 接口。MediaAdapter 使得 AudioPlayer 可以播放 MP4 和 VLC 格式的文件,尽管 AudioPlayer

为什么适配器类也要实现MediaPlayer接口?

在适配器模式(Adapter Pattern)中,适配器类实现目标接口(在这个例子中是 MediaPlayer 接口)是非常关键的一步。下面我会详细解释为什么适配器类需要实现目标接口,结合本例进一步展开。

适配器模式的基本概念

适配器模式的核心目标是使得两个接口不兼容的类能够一起工作。适配器类充当“桥梁”的角色,它通过实现目标接口,将现有的、与目标接口不兼容的类适配成我们需要的接口形式。

目标接口(MediaPlayer

在本例中,MediaPlayer 是客户端期望使用的接口,它提供了一个统一的播放方法 play(),客户端通过这个接口来播放音频,不关心具体的实现细节。

interface MediaPlayer {
    void play(String audioType, String fileName);
}
原接口(AdvancedMediaPlayer

原接口 AdvancedMediaPlayer 提供了播放 MP4 和 VLC 格式文件的接口。这个接口并不与 MediaPlayer 接口兼容,无法直接被客户端使用。

interface AdvancedMediaPlayer {
    void playVlc(String fileName);
    void playMp4(String fileName);
}
适配器类(MediaAdapter

适配器类的作用是将不兼容的 AdvancedMediaPlayer 类适配为 MediaPlayer 接口。为了使适配器能够统一提供 MediaPlayerplay() 方法,它需要实现 MediaPlayer 接口。

class MediaAdapter 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) {
        advancedMusicPlayer.play(fileName);  // 调用适配器的 play 方法
    }
}

为什么适配器需要实现 MediaPlayer 接口

  1. 统一接口,符合客户端需求

    • 客户端希望通过 MediaPlayer 接口来播放各种格式的音频文件。AudioPlayer 类和 MediaAdapter 都需要实现 MediaPlayer 接口。
    • AudioPlayer 类直接实现 MediaPlayer 接口,处理 MP3 文件。而对于 MP4 和 VLC 格式的音频文件,AudioPlayer 则委托给 MediaAdapter
    • 如果 MediaAdapter 不实现 MediaPlayer 接口,客户端就无法通过统一的接口来播放所有音频格式,AudioPlayer 也无法直接调用它。
  2. 符合适配器模式的设计原则

    • 适配器模式的本质是“将一个接口适配成另一个接口”。适配器类需要实现目标接口(即 MediaPlayer 接口),才能充当桥梁,把不兼容的接口(如 AdvancedMediaPlayer)适配为客户端需要的接口。
    • 客户端通过目标接口(MediaPlayer)与 AudioPlayerMediaAdapter 交互,客户端不需要关心背后具体的实现细节(如 VlcPlayerMp4Player)。
  3. 增强灵活性和可扩展性

    • 通过让 MediaAdapter 实现 MediaPlayer 接口,系统的设计更加灵活。当需要添加新的音频格式支持时,我们只需要创建一个新的播放器(比如 AviPlayer)并让它实现 AdvancedMediaPlayer 接口。然后,只需扩展 MediaAdapter 来支持新格式,无需修改客户端的代码。
    • 适配器的职责是将一个接口转换为另一个接口。如果 MediaAdapter 没有实现 MediaPlayer 接口,那么 AudioPlayer 就无法通过 MediaAdapter 播放新格式的文件,从而破坏了代码的可扩展性。
  4. 与客户端的耦合

    • 由于 MediaAdapter 实现了 MediaPlayer 接口,客户端代码(例如 AudioPlayer)无需关心其背后是如何实现的,只要通过 MediaPlayer 接口进行调用即可。客户端依赖于接口而非实现,这符合面向接口编程的设计思想。
    • 如果适配器类不实现 MediaPlayer 接口,客户端将无法调用它的 play() 方法,客户端就会与 AdvancedMediaPlayer 紧密耦合,失去了适配器的意义。

总结

适配器类需要实现 MediaPlayer 接口,目的是:

  • 统一接口:使得 MediaAdapter 能与 AudioPlayer 一样,通过 MediaPlayer 接口来播放音频文件,客户端代码不会因为使用了不同类型的播放器而发生变化。
  • 遵循适配器模式的原则:适配器模式的核心是将一个接口转换成另一个接口,适配器类通过实现目标接口来实现这一转换。
  • 提高灵活性和扩展性:通过实现目标接口,适配器类可以轻松支持新格式,增强系统的可扩展性。

通过这种设计,客户端的代码不会受到播放器具体实现的影响,保持了系统的解耦,也为未来的扩展提供了便利。

若有新的媒体格式播放需求,该如何修改该适配器?

如果有新的媒体格式播放需求(比如新增 .avi 格式支持),你可以按照以下步骤修改适配器模式中的代码来支持新的格式。我们将以 .avi 格式为例,来展示如何修改适配器和相关代码。

1. 修改 AdvancedMediaPlayer 接口

首先,在 AdvancedMediaPlayer 接口中增加一个新的方法 playAvi,用于播放 .avi 格式的文件。

// 原有的接口
interface AdvancedMediaPlayer {
    void playVlc(String fileName);
    void playMp4(String fileName);
    // 新增支持的 .avi 格式
    void playAvi(String fileName);  
}

2. 新增实现类 AviPlayer

接下来,为 .avi 格式创建一个新的播放器实现类 AviPlayer,实现 AdvancedMediaPlayer 接口,并实现 playAvi 方法。

class AviPlayer implements AdvancedMediaPlayer {
    @Override
    public void playVlc(String fileName) {
        // 不支持 VLC 格式
    }

    @Override
    public void playMp4(String fileName) {
        // 不支持 MP4 格式
    }

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

3. 修改 MediaAdapter

为了支持 .avi 格式,你需要修改 MediaAdapter 类,增加对 .avi 格式的适配处理。我们可以通过在 MediaAdapter 构造函数中判断传入的格式,并创建对应的播放器对象。

class MediaAdapter implements MediaPlayer {
    AdvancedMediaPlayer advancedMusicPlayer;

    public MediaAdapter(String audioType) {
        if (audioType.equalsIgnoreCase("vlc")) {
            advancedMusicPlayer = new VlcPlayer();
        } else if (audioType.equalsIgnoreCase("mp4")) {
            advancedMusicPlayer = new Mp4Player();
        } else if (audioType.equalsIgnoreCase("avi")) {
            advancedMusicPlayer = new AviPlayer();  // 新增对 .avi 格式的支持
        }
    }

    @Override
    public void play(String audioType, String fileName) {
        if (audioType.equalsIgnoreCase("vlc")) {
            advancedMusicPlayer.playVlc(fileName);
        } else if (audioType.equalsIgnoreCase("mp4")) {
            advancedMusicPlayer.playMp4(fileName);
        } else if (audioType.equalsIgnoreCase("avi")) {
            advancedMusicPlayer.playAvi(fileName);  // 调用新实现的 playAvi 方法
        }
    }
}

4. 修改 AudioPlayerClient

最后,在 AudioPlayerClient 中,你需要确保 .avi 格式的播放器被正确使用。当你通过 MediaAdapter 类来适配 .avi 格式时,你可以直接使用它播放 .avi 文件。

class AudioPlayerClient {
    MediaPlayer audioPlayer;

    public AudioPlayerClient(MediaPlayer audioPlayer) {
        this.audioPlayer = audioPlayer;
    }

    public void playMedia(String audioType, String fileName) {
        audioPlayer.play(audioType, fileName);
    }
}

5. 客户端调用

现在,客户端可以通过 MediaAdapter 来支持播放 .avi 格式的音频文件了。你只需将 .avi 格式的请求传递给 AudioPlayerClientMediaAdapter 会根据格式自动选择正确的播放器。

public class Main {
    public static void main(String[] args) {
        AudioPlayerClient audioPlayerClient = new AudioPlayerClient(new AudioPlayer());

        // 测试 MP3 格式
        audioPlayerClient.playMedia("mp3", "beyond the horizon.mp3");

        // 测试 VLC 格式
        audioPlayerClient.playMedia("vlc", "far far away.vlc");

        // 测试 AVI 格式
        audioPlayerClient.playMedia("avi", "mind me.avi");  // 新增的 AVI 格式
    }
}

代码总结

  1. 修改接口:在 AdvancedMediaPlayer 接口中新增对 .avi 格式播放的支持(即增加 playAvi 方法)。
  2. 新增实现类:为 .avi 格式创建一个新的播放器类 AviPlayer,并实现播放逻辑。
  3. 修改适配器:在 MediaAdapter 中增加对 .avi 格式的支持,在 play 方法中进行格式判断,并调用 AviPlayerplayAvi 方法。
  4. 客户端调用:客户端不需要关心具体实现,只需调用 MediaAdapter 来处理不同的音频格式。

优势

  • 扩展性强:当你需要支持新的音频格式时,只需添加一个新的实现类并修改适配器类,而不需要修改现有的客户端代码或其他播放器类。
  • 符合开闭原则:现有的代码对修改是封闭的,对扩展是开放的。你只需扩展系统,而不需要修改现有的功能。
  • 职责分离清晰:每个播放器类只负责一个格式的播放,适配器类负责将客户端请求转发到正确的播放器类,代码结构更加清晰。

这样,如果以后还需要增加新的格式,只需要按照这种方式新增相关的播放器实现和适配逻辑,而不需要对现有代码进行过多修改,确保系统能够灵活扩展。

面向接口的编程与适配器设计模式

面向接口的编程(Interface-based Programming)和适配器模式(Adapter Pattern)都与接口有很大的关系,它们的设计理念和应用场景有所不同。我们可以通过以下几个方面来对比这两者:

1. 基本概念

面向接口的编程:

面向接口的编程是一种设计方法,它强调通过接口来抽象对象的行为,定义不同对象可以遵循的规则或契约。接口只定义行为,不关心具体的实现。

  • 目标: 实现灵活、解耦和可扩展的设计,减少模块之间的耦合度。
  • 特点: 类之间通过接口进行交互,具体的实现可以替换而不影响其他部分的代码。
适配器模式:

适配器模式是一种结构型设计模式,它的目的是将一个类的接口转换成客户期望的另一个接口。适配器模式通过引入适配器类来“适配”现有的接口,使得原本由于接口不兼容而无法一起工作的类可以一起工作。

  • 目标: 通过一个适配器类,处理接口不兼容的问题,让不同的接口能够共同工作。
  • 特点: 适配器类在原有接口和目标接口之间进行转换。

2. 结构差异

面向接口的编程:
  • 接口定义: 在面向接口的编程中,我们定义多个接口,多个类可以实现不同的接口。
  • 接口实现: 类根据需要实现接口中的方法。不同的实现类提供不同的行为,而客户端代码只依赖于接口,而不关心具体的实现。
// 面向接口的编程:定义接口并实现
interface MediaPlayer {
    void play(String audioType, String 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);
        }
    }
}
适配器模式:
  • 目标接口和适配接口: 适配器模式涉及两个接口:目标接口(客户端期望的接口)和 适配接口(被适配的接口)。适配器类将被适配的接口转化为目标接口,确保客户端可以以一致的方式调用。
  • 适配器实现: 适配器类实现目标接口,并将客户端请求转换为被适配接口的方法调用。
// 适配器模式:适配器将不同格式的播放器统一适配为目标接口 MediaPlayer
interface MediaPlayer {
    void play(String audioType, String fileName);
}

interface AdvancedMediaPlayer {
    void playVlc(String fileName);
    void playMp4(String fileName);
}

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

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

class MediaAdapter implements MediaPlayer {
    AdvancedMediaPlayer advancedMusicPlayer;

    public MediaAdapter(String audioType) {
        if(audioType.equalsIgnoreCase("vlc")){
            advancedMusicPlayer = new VlcPlayer();
        }
    }

    @Override
    public void play(String audioType, String fileName) {
        if(audioType.equalsIgnoreCase("vlc")){
            advancedMusicPlayer.playVlc(fileName);
        }
    }
}

3. 核心目的和适用场景

面向接口的编程:
  • 目的: 提供高内聚、低耦合的设计。通过接口定义一组行为规范,允许不同的实现类提供具体的实现。接口的抽象为系统的扩展提供了灵活性。
  • 适用场景:
    • 你需要一个类的多个实现,并且希望能够在运行时根据需要切换实现。
    • 你希望实现代码复用和解耦,使得类和类之间的依赖最小化。
    • 比如,多个类实现一个通用的 MediaPlayer 接口,处理不同格式的音频播放。
适配器模式:
  • 目的: 解决接口不兼容的问题。通过引入适配器类来将不兼容的接口转换为目标接口,使得原本不能协同工作的类能够一起工作。
  • 适用场景:
    • 你有一个现有的类(比如第三方库提供的类),它有一个不符合你当前系统设计的接口,但你无法更改这个类。
    • 你希望将现有的类与新的接口或者类集成,且不想修改现有的类代码。
    • 比如,你需要通过适配器将旧的 .mp3 播放器与新接口集成。

4. 代码复用 vs. 代码桥接

面向接口的编程:
  • 代码复用: 面向接口的编程通过接口和抽象类为不同的实现提供复用机会。不同类的实现可以根据需求复用相同的接口。
  • 举例: AudioPlayer 类可以复用 MediaPlayer 接口并实现多种格式的播放,如 .mp3.mp4
适配器模式:
  • 代码桥接: 适配器模式并不要求重写所有的实现类,而是通过创建适配器类,来在两个不同接口间建立桥梁。适配器类实现目标接口并委托给被适配类来执行实际的工作。
  • 举例: MediaAdapter 类作为适配器,桥接了 MediaPlayer 接口和 AdvancedMediaPlayer 接口之间的差异,将 .vlc 播放的请求委托给 VlcPlayer 类的 playVlc 方法。

5. 灵活性与扩展性

面向接口的编程:
  • 灵活性: 高度依赖接口定义,使得代码能够更容易被扩展和替换。客户端与接口解耦,允许替换不同的实现类。
  • 扩展性: 新的实现类可以根据需要随时添加,不影响已有代码,只要新的实现类遵循相同的接口。
适配器模式:
  • 灵活性: 在不修改现有代码的前提下,能够使现有类与新接口兼容。适配器模式提供了兼容性,即使原始接口和目标接口之间不兼容,也能通过适配器来解决。
  • 扩展性: 如果需要支持新的格式,适配器模式允许你添加新的适配器类,而不需要修改现有的代码。

6. 代码实例对比

面向接口编程示例:
interface MediaPlayer {
    void play(String audioType, String 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);
        }
    }
}
适配器模式示例:
interface MediaPlayer {
    void play(String audioType, String fileName);
}

interface AdvancedMediaPlayer {
    void playVlc(String fileName);
    void playMp4(String fileName);
}

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

    @Override
    public void playMp4(String fileName) {}
}

class MediaAdapter implements MediaPlayer {
    AdvancedMediaPlayer advancedMusicPlayer;

    public MediaAdapter(String audioType) {
        if(audioType.equalsIgnoreCase("vlc")){
            advancedMusicPlayer = new VlcPlayer();
        }
    }

    @Override
    public void play(String audioType, String fileName) {
        if(audioType.equalsIgnoreCase("vlc")){
            advancedMusicPlayer.playVlc(fileName);
        }
    }
}

7. 总结

特性面向接口的编程适配器模式
核心目标提高灵活性和解耦,通过接口实现行为抽象通过适配器将不兼容的接口转为目标接口
主要目的实现代码的可扩展性和可替换性解决接口不兼容的问题
应用场景需要多种类实现相同接口时需要集成不同接口(特别是现有接口)时
设计特点定义统一的接口,多个类实现使用适配器类将不同接口桥接为一个统一接口

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2269578.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java项目之高校心理教育辅导系统的设计与实现(springboot+mybatis+mysql)

风定落花生,歌声逐流水,大家好我是风歌,混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的闲一品交易平台。项目源码以及部署相关请联系风歌,文末附上联系信息 。 项目简介: 高校心理教育辅导系统的设…

Cesium 实战 27 - 三维视频融合(视频投影)

Cesium 实战 27 - 三维视频融合(视频投影) 核心代码完整代码在线示例在 Cesium 中有几种展示视频的方式,比如墙体使用视频材质,还有地面多边形使用视频材质,都可以实现视频功能。 但是随着摄像头和无人机的流行,需要视频和场景深度融合,简单的实现方式则不能满足需求。…

MAC系统QT图标踩坑记录

MAC系统QT图标踩坑记录 1. 准备图标1.1 方法一:下载准备好的图标1.2 方法二:自己生成图标1.2.1 准备一个png文件1.2.2 用sips生成不同大小的图片1.2.3 用iconutil生成图标文件 2. 配置图标2.1. 把图标改命成自己想要的名字,如icon.icns&#…

ARM64 Windows 10 IoT工控主板运行x86程序效率测试

ARM上的 Windows 10 IoT 企业版支持仿真 x86 应用程序,而 ARM上的 Windows 11 IoT 企业版则支持仿真 x86 和 x64 应用程序。英创推出的名片尺寸ARM64工控主板ESM8400,可预装正版Windows 10 IoT企业版操作系统,x86程序可无需修改而直接在ESM84…

汽车损坏识别检测数据集,使用yolo,pasical voc xml,coco json格式标注,6696张图片,可识别11种损坏类型,识别率89.7%

汽车损坏识别检测数据集,使用yolo,pasical voc xml,coco json格式标注,6696张图片,可识别11种损坏类型损坏: 前挡风玻璃(damage-front-windscreen ) 损坏的门 (damaged-d…

西门子1200PLC和三菱FX3系列PLC接线方法

1、西门子1200PLC接线方法。* 2、从三菱官方手册查询得知,S/S公共端有两种接法,但是为了与西门子1200接法保持一致,所以也建议采用S/S公共点0V的接法。 **【总结】 三菱输入端采用公共点接0V接法建议提升至公司内部标准规范: …

一文理清JS中获取盒子宽高各方法的差异

前言 这段时间在研究一个反爬产品,环境检测用到了很多个盒子宽高取值方法,如window.outerWidth、window.screen.availWidth,各个方法取值结果不大相同,在此记录下遇到的方法。 各宽方法区别 这里就讲解下各宽度方法的区别&…

AWVS安装使用教程

一、AWVS工具介绍及下载 AWVS工具介绍 AWVS(Acunetix Web Vulnerability Scanner)是一款知名的网络漏洞扫描工具,它通过网络爬虫测试你的web站点,检测流行安全漏洞,可以检查SQL注入漏洞,也可以检查跨站脚…

用Python操作字节流中的Excel文档

Python能够轻松地从字节流中加载文件,在不依赖于外部存储的情况下直接对其进行读取、修改等复杂操作,并最终将更改后的文档保存回字节串中。这种能力不仅极大地提高了数据处理的灵活性,还确保了数据的安全性和完整性,尤其是在网络…

【LeetCode】928、尽量减少恶意软件的传播 II

【LeetCode】928、尽量减少恶意软件的传播 II 文章目录 一、并查集1.1 并查集 二、多语言解法 一、并查集 1.1 并查集 先把普通点, build 并查集遍历每个源头点, 找源头点附近的点所在的集合, 传染该集合拯救节点 3.1 若该节点 所在集合, 从未被感染过, 则开始感染 3.2 若该节…

(NDSS2024)论文阅读——仅低质量的训练数据?用于检测加密恶意网络流量的稳健框架

文章基本信息 作者:Yuqi Qing et al. (清华大学李琦团队) 代码 文章 摘要 存在问题:收集包含足够数量的带有正确标签的加密恶意数据的训练数据集是具有挑战性的,当使用低质量的训练数据训练机器学习模型时&#xff…

如何将CSDN文章 导出为 PDF文件

一、首先,打开我们想要导出为 PDF格式的 CSDN文章,以下图为例。 二、按 F12 调出浏览器调式模式后,选择 控制台 三、在控制台处粘贴代码 代码: (function(){ use strict;var articleBox $("div.article_content"…

YApi接口管理平台本地搭建方法介绍

YApi是一个免费开源的API管理平台,开发人员可用它来管理、调试接口,并且提供了API文档管理和测试功能,具有友好的UI页面,本文介绍Linux环境如何安装部署YApi接口管理平台。 目录 1 环境准备2 安装部署2.1 安装nodejs2.2 安装 Mong…

案例分析-采样率对模拟链路的带宽的影响

目录 问题来源: 情况分析: 总结 问题来源: 在进行模拟带宽调整时,发现设计值 与实测值,不一样,就这一问题,进行详细分析。 情况分析: 在本项目中,采用巴特沃兹四阶滤波器,设计带宽350M,改滤波器设计可以采用fiter solution工具进行设计,实测值仅仅260M,因此针…

Huggingface Trending!可控人物图像生成统一框架Leffa,可精确控制虚拟试穿和姿势转换!

今天给大家介绍一个Huggingface上虚拟试穿的热门项目Leffa,Leffa是一个可控人物图像生成的统一框架,可以精确操纵外观(即虚拟试穿)和姿势(即姿势转换)。从效果看生成效果很不错! 相关链接 论文&…

memcached的基本使用

memcached是一种基于键值对的内存数据库,一般应用于缓存数据,提高数据访问速度,减轻后端数据库压力。 安装 这里以Ubuntu为例,其他系统安装方法请看官方文档。 sudo apt-get update sudo apt-get install memcached启动 memca…

ROS话题通信

1 .理论模型 话题通信实现模型是比较复杂的,该模型如下图所示,该模型中涉及到三个角色: ROS Master (管理者)Talker (发布者)Listener (订阅者) ROS Master 负责保管 Talker 和 Listener 注册的信息,并匹配话题相同的 Talker 与 Listener,…

经验证:将数据从索尼传输到Android的 4 种方法

概括 像Android Galaxy S20 这样的新型Android智能手机很酷,但除了将数据从索尼传输到Android之外。众所周知,旧的索尼手机上存储着大量的文件,因此将数据从旧的索尼手机传输到新的Android手机非常重要。为了解决这个问题,我们做…

VITUREMEIG | AR眼镜 算力增程

根据IDC发布的《2024年第三季度美国AR/VR市场报告》显示,美国市场AR/VR总出货量增长10.3%。其中,成立于2021年的VITURE增长速度令人惊艳,同比暴涨452.6%,成为历史上增长最快的AR/VR品牌。并在美国AR领域占据了超过50%的市场份额&a…

JavaSpring AI与阿里云通义大模型的集成使用Java Data Science Library(JDSL)进行数据处理

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默, 忍不住分享一下给大家。点击跳转到网站 学习总结 1、掌握 JAVA入门到进阶知识(持续写作中……) 2、学会Oracle数据库入门到入土用法(创作中……) 3、手把…