适配器模式概述

大体介绍

适配器模式(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;@Overridepublic 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 {@Overridepublic void playVlc(String fileName) {System.out.println("Playing vlc file. Name: " + fileName);}@Overridepublic void playMp4(String fileName) {// 无法播放 MP4 文件}
}// 具体类:Mp4Player(实现AdvancedMediaPlayer)
class Mp4Player implements AdvancedMediaPlayer {@Overridepublic void playVlc(String fileName) {// 无法播放 VLC 文件}@Overridepublic 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();}}@Overridepublic 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;@Overridepublic 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();}}@Overridepublic 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();}}@Overridepublic 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 {@Overridepublic void playVlc(String fileName) {// 不支持 VLC 格式}@Overridepublic void playMp4(String fileName) {// 不支持 MP4 格式}@Overridepublic 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 格式的支持}}@Overridepublic 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 {@Overridepublic 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 {@Overridepublic void playVlc(String fileName) {System.out.println("Playing vlc file. Name: " + fileName);}@Overridepublic void playMp4(String fileName) {// Do nothing}
}class MediaAdapter implements MediaPlayer {AdvancedMediaPlayer advancedMusicPlayer;public MediaAdapter(String audioType) {if(audioType.equalsIgnoreCase("vlc")){advancedMusicPlayer = new VlcPlayer();}}@Overridepublic 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 {@Overridepublic 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 {@Overridepublic void playVlc(String fileName) {System.out.println("Playing vlc file. Name: " + fileName);}@Overridepublic void playMp4(String fileName) {}
}class MediaAdapter implements MediaPlayer {AdvancedMediaPlayer advancedMusicPlayer;public MediaAdapter(String audioType) {if(audioType.equalsIgnoreCase("vlc")){advancedMusicPlayer = new VlcPlayer();}}@Overridepublic void play(String audioType, String fileName) {if(audioType.equalsIgnoreCase("vlc")){advancedMusicPlayer.playVlc(fileName);}}
}

7. 总结

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

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

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

相关文章

计算机专业考研 408 学科学习方法

计算机专业考研 408 学科涵盖数据结构、计算机组成原理、操作系统和计算机网络四门核心课程,内容多且难度大。但只要掌握科学的学习方法,便能化繁为简,稳步提升。以下为大家详细介绍 408 学科的学习方法。 一、基础夯实阶段:全面…

C++ 设计模式:命令模式(Command Pattern)

链接:C 设计模式 链接:C 设计模式 - 访问器模式 命令模式(Command Pattern)是一种行为型设计模式,它将请求封装成一个对象,从而使你可以用不同的请求对客户进行参数化,对请求排队或记录请求日志…

html+css+js网页设计 美食 美食4个页面带js

htmlcssjs网页设计 美食 美食4个页面带js 网页作品代码简单,可使用任意HTML辑软件(如:Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑等操作)。 获取源码 1&#…

swagger,showdoc,apifox,Mock 服务,dubbo,ZooKeeper和dubbo的关系

Swagger、ShowDoc 和 Apifox 之间的区别与优势 Swagger、ShowDoc 和 Apifox 都是用于 API 文档管理和测试的工具,但它们各有特色和适用场景。以下是详细的比较,并附上每个工具的具体用法示例。 1. Swagger 特点与优势: 广泛采用: Swagger…

边沿检测电路漏检原因分析

边沿检测电路漏检原因分析 常用结构如下&#xff1a; module edge_detect1( input clk, input signal, output pe, //上升沿 output ne, //下降沿 output de //双边沿 );reg reg1;always(posedge clk) beginreg1 < signal; endassign pe (~reg1) & signal; assign…

嵌入式硬件杂谈(七)IGBT MOS管 三极管应用场景与区别

引言&#xff1a;在现代嵌入式硬件设计中&#xff0c;开关元件作为电路中的重要组成部分&#xff0c;起着至关重要的作用。三种主要的开关元件——IGBT&#xff08;绝缘栅双极型晶体管&#xff09;、MOSFET&#xff08;金属氧化物半导体场效应晶体管&#xff09;和三极管&#…

鸿蒙开发:了解正则表达式

前言 从给出的文本中&#xff0c;按照既定的相关规则&#xff0c;匹配出符合的数据&#xff0c;其中的规则就是正则表达式&#xff0c;使用正则表达式&#xff0c;可以使得我们用简洁的代码就能实现一定复杂的逻辑&#xff0c;比如判断一个邮箱账号是否符合正常的邮箱账号&…

Kafka的acks机制和ISR列表

Kafka 是一个流行的分布式流处理平台&#xff0c;用于构建实时数据流管道和应用程序。在 Kafka 中&#xff0c;acks 机制和 ISR&#xff08;In-Sync Replicas&#xff09;列表是两个重要的概念&#xff0c;它们共同确保消息的持久性和可靠性。 acks 机制 acks 机制是 Kafka 生…

在 Ubuntu 下通过 Docker 部署 Caddy 服务器

嘿&#xff0c;伙伴们&#xff01;今天我们来聊聊如何在 Ubuntu 系统下通过 Docker 部署 Caddy 服务器。Caddy 是一个现代的 Web 服务器&#xff0c;支持自动 HTTPS&#xff0c;简单易用&#xff0c;特别适合快速搭建网站。而 Docker 则是一个让你可以隔离和管理应用的神器。结…

计算机网络•自顶向下方法:网络层介绍、路由器的组成

网络层介绍 网络层服务&#xff1a;网络层为传输层提供主机到主机的通信服务 每一台主机和路由器都运行网络层协议 发送终端&#xff1a;将传输层报文段封装到网络层分组中&#xff0c;发送给边缘路由器路由器&#xff1a;将分组从输入链路转发到输出链路接收终端&#xff1…

Linux top指令

top指令概述 top 是 Linux 系统中用于实时监控系统性能和进程信息的命令&#xff0c;功能强大且灵活。它提供了系统资源的动态视图&#xff0c;包括 CPU、内存、运行中的进程等。 这个指令可以说是Linux中最基本的工具了&#xff0c;用来监视系统的实时运行状态&#xff0c;类…

Qt监控系统放大招/历经十几年迭代完善/多屏幕辅屏预览/多层级设备树/网络登录和回放

一、前言说明 近期对视频监控系统做了比较大的更新升级&#xff0c;主要就是三点&#xff0c;第一点就是增加了辅屏预览&#xff0c;这个也是好多个客户需要的功能&#xff0c;海康的iVMS-4200客户端就有这个功能&#xff0c;方便在多个屏幕打开不同的视频进行查看&#xff0c…

网络原理(六): UDP 协议

目录 1. UDP 协议 1.1 协议特点 1.2 协议报文格式 1.2.1 UDP 长度 1.2.2 校验和 1. UDP 协议 在进行网络编程时, 我们已经对 UDP 协议进行了简单了解. 并且应用层的很多操作, 需要调用传输层的提供的接口, 基于 socket api 来进行完成的. 1.1 协议特点 UDP 协议具有以…

前端页面展示本电脑的摄像头,并使用js获取摄像头列表

可以通过 JavaScript 使用 navigator.mediaDevices.enumerateDevices() 获取电脑上的摄像头列表。以下是一个示例代码&#xff0c;可以展示摄像头列表并选择进行预览。 HTML JavaScript 实现摄像头列表展示和预览 <!DOCTYPE html> <html lang"zh-CN">…

【漫话机器学习系列】028.CP

Mallows’ Cp&#xff1a;标准化公式解析与应用 Mallows’ Cp 是一种常用的模型选择工具&#xff0c;用于在一系列候选模型中权衡拟合度和复杂性&#xff0c;帮助我们选择性能最优的模型。本文将基于其标准化公式展开详细解析&#xff0c;并探讨其应用场景、实现方法、优点与局…

期末算法分析程序填空题

目录 5-1 最小生成树&#xff08;普里姆算法&#xff09; 5-2 快速排序&#xff08;分治法&#xff09; 输入样例&#xff1a; 输出样例&#xff1a; 5-3 归并排序(递归法) 输入样例&#xff1a; 输出样例&#xff1a; 5-4 求解编辑距离问题&#xff08;动态规划法&#xff09;…

深入解析 Conda 安装的默认依赖包及其作用:conda create安装了哪些包(中英双语)

深入解析 Conda 安装的默认依赖包及其作用 当我们使用 Conda 创建新环境时&#xff0c;例如执行命令&#xff1a; conda create -n olmes python3.10Conda 会自动为我们安装一系列基础依赖包&#xff0c;保证 Python 环境能够正常运行。这些包不仅是我们开发的基础工具&#…

Mac、Linux命令

Linux 查本机IP&#xff1a;ip addr 查询文件里符合条件的字符串&#xff1a;grep Mac 查本机IP&#xff1a;ipconfig

Visual Studio 中增加的AI功能

前言&#xff1a; 人工智能的发展&#xff0c;在现在&#xff0c;编程技术的IDE里面也融合了AI的基本操做。本例&#xff0c;以微软的Visual Studio中的人工智能的功能介绍例子。 本例的环境&#xff1a; Visual Studio 17.12 1 AI 智能变量检测&#xff1a; 上图展示了一…

江科大学习笔记之——HAL库点亮一个LED灯

HAL三步走&#xff1a;1.建工程。2.设配置。3.写代码 一.建立工程 HAL库写法 点击FinIsh 2.配置时钟 2 、设置配置参数 把模块.C.h单独设置文件 生产代码 三写代码 控制GPIO就三步 1.RCC时钟使能 2.结构体配置GPIO寄存器 3.控制GPIO值 上面的步骤已经把前两步的配置完成了接下…