当前位置: 首页 > news >正文

适配器模式概述

大体介绍

适配器模式(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. 总结

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

相关文章:

适配器模式概述

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

跟着问题学3.1——R-CNN模型详解

R-CNN解决什么问题 前面我们介绍了经典的网络模型如AlexNet,VGG,ResNet等,这些模型要解决的任务都是分类问题,即输入一张图片,判断图片上是什么类别的物体,而且一般是单个物体。但实际中,我们会遇到一张图片上有多个或…...

微服务-1 认识微服务

目录​​​​​​​ 1 认识微服务 1.1 单体架构 1.2 微服务 1.3 SpringCloud 2 服务拆分原则 2.1 什么时候拆 2.2 怎么拆 2.3 服务调用 3. 服务注册与发现 3.1 注册中心原理 3.2 Nacos注册中心 3.3 服务注册 3.3.1 添加依赖 3.3.2 配置Nacos 3.3.3 启动服务实例 …...

25秋招面试总结

秋招从八月底开始,陆陆续续面试了不少,现在也是已经尘埃落定,在这里做一些总结一些我个人的面试经历 腾讯 腾讯是我最早面试的一家,一开始捞我面试的是数字人民币,安全方向的岗位,属于腾讯金融科技这块。…...

【C#学习——特性】

前言 C#特性学习、主要是用在数据库连接时如何动态创建对应的表,正常开发应该使用如Entity Framework等ORM框架实现自动创建生成。 代码 1、声明特性 [AttributeUsage(AttributeTargets.Property)] public class PrimaryKeyAttribute : Attribute { }[AttributeUs…...

Appscan扫出API成批分配问题解决方案

漏洞条件: 请求json参数不是接收参数的javabean及其父类中的任意属性。 意思就是:我javaben里面没有这个参数 你缺传递过来了 例如我只需要pageNum pageSize 你还传了role:admin 那么这样就有可能导致致特权升级、数据篡改、绕过安全机制 解决方案&am…...

STM32-笔记14-排队控制系统

一、项目需求 1. 红外传感器检测有人通过并计数; 2. 计数值显示在LCD1602 3. 允许通过时,LED1闪烁,蜂鸣器不响,继电器不闭合; 4. 不允许通过时,LED2闪烁,蜂鸣器响,继电器闭合&#…...

【时间之外】IT人求职和创业应知【80】-特殊日子

目录 北京冬季招聘会 OpenAI CEO炮轰马斯克 英伟达推出全新AI芯片B300 莫欢喜,总成空。本周必须要谨行慎言。 感谢所有打开这个页面的朋友。人生不如意,开越野车去撒野,会害了自己,不如提升自己。提升自己的捷径就是学习和思考…...

【GlobalMapper精品教程】090:合并多个面状图斑(以一个镇的多个村不动产宗地为例)

本文讲述在Globalmapper中,合并多个面状图斑的方法,以一个镇的多个村不动产宗地为例(假设一个镇的多个村的不动产宗地数据是分别存储在不同的村子矢量数据中,此时需要合并),点状和线状的操作方法类似。 文章目录 一、加载数据二、数据分析三、合并图斑四、注意事项一、加…...

ffmpeg之播放一个yuv视频

播放YUV视频的步骤 初始化SDL库: 目的:确保SDL库正确初始化,以便可以使用其窗口、渲染和事件处理功能。操作:调用 SDL_Init(SDL_INIT_VIDEO) 来初始化SDL的视频子系统。 创建窗口用于显示YUV视频: 目的:…...

在国产电脑上运行PDFSAM软件使用pdf分割合并交替混合处理pdf文档

软件下载地址: https://sourceforge.net/projects/pdfsam/files/ 需要注意事项,系统需要java环境,确认系统有java环境,根据软件版本需求安装对应的java运行环境。 下载pdfsam-4.3.4-linux.tar.gz安装包,解压,将runt…...

总结一下本次使用docker部署遇到的问题

1.Invalid bound statement (not found):异常 解决:原因是Dao层与动态Sql映射文件名字没有对应 2.element-plus的upload组件文件上传不成功 因为是直接请求后端不是统一的api前缀,所以nginx需要额外配置跨域 3.文件上传问题 描述:当时文…...

c#泛型学习

使用泛型的优点:使用泛型的好处包括类型安全、代码重用和性能优化。 在C#中,泛型是一种强大的工具,它允许你在编写类、接口、方法和委托时定义类型参数。这些类型参数在实例化泛型类型或调用泛型方法时被具体的类型所替代。 1. 泛型类 泛型…...

十二月第五周python

第一个程序,熟悉转换器,把加法计算器变成exe# // 1,制作加法计算器, # 输入两个数字得到相加结果并输出aint(input("输入数字:"))#int()是把输入的内容转换成整数, bint(input("输入数字:&…...

Unity中如何修改Sprite的渲染网格

首先打开SpriteEditor 选择Custom OutLine,点击Genrate 则在图片边缘会出现边缘线,调整白色小方块可以调整边缘 调整后,Sprite就会按照调整后的网格渲染了。 如何在UI中使用? 只要在UI的Image组件中选择Use Sprite Mesh 即可 结果&#xff1…...

修复OpenHarmony系统相机应用横屏拍照按钮点不到的问题

适配OpenHarmony系统相机应用横屏UI, 相关pr: https://gitee.com/openharmony/applications_camera/pulls/233/files 适配效果 如何安装 编译好的hap提供在附件中 1.预置在源码,随固件安装 2.安装hap hdc shell "mount -o remount,rw /"…...

keepass实现google自输入_SSH_TELNET_RDP联动

涉及到的是使用开源密码管理工具KeePass结合特定插件实现自动化密码填充的功能,特别是在谷歌浏览器中的应用。KeePass是一款强大的密码管理软件,它允许用户安全地存储各种账号的用户名和密码,并通过加密保护这些敏感信息。 1. keepass安装及配…...

电脑缺失sxs.dll文件要怎么解决?

一、文件丢失问题:以sxs.dll文件缺失为例 当你在运行某个程序时,如果系统提示“找不到sxs.dll文件”,这意味着你的系统中缺少了一个名为sxs.dll的动态链接库文件。sxs.dll文件通常与Microsoft的.NET Framework相关,是许多应用程序…...

Python实现机器学习驱动的智能医疗预测模型系统的示例代码框架

以下是一个使用Python实现机器学习驱动的智能医疗预测模型系统的示例代码框架。这个框架涵盖了数据收集(爬虫)、数据清洗和预处理、模型构建(决策树和神经网络)以及模型评估的主要步骤。 1. 数据收集(爬虫&#xff09…...

Vue3生态: 使用Vite进行高速开发

Vue3生态: 使用Vite进行高速开发 一、Vite概述 什么是Vite 法语意为 "快速")是一个为现代浏览器原生开发提供服务的构建工具。它使用ES模块作为原生浏览器加载工具,利用浏览器去解析 import 的方式加载文件,极大地加快了应用的启动…...

Android MQTT关于断开连接disconnect报错原因

最近项目遇到一个需求,就是在登录状态的时候。才能接收到消息。所有我在上线,下线状态的时候。做了MQTT断开和连接的动作。然后就是发生了。我们标题的这关键点了。直接报错了。报错的内容如下: MqttAndroidClient unregisterRecevicer afte…...

YOLO11全解析:从原理到实战,全流程体验下一代目标检测

前言 一、模型介绍 二、网络结构 1.主干网络(Backbone) 2.颈部网络(Neck) 3.头部网络(Head) 三、算法改进 1.增强的特征提取 2.优化的效率和速度 3.更高的准确性与更少的参数 4.环境适应性强 5.…...

python中函数的用法总结(二阶段)

话接上回,继续讲下函数的用法 10. 函数的注解(Function Annotations) Python 3 引入了函数注解,允许你在函数定义时给参数和返回值添加注解。注解并不影响函数的实际行为,它们更多地用于代码的可读性、文档生成以及静…...

1082 射击比赛

本题目给出的射击比赛的规则非常简单,谁打的弹洞距离靶心最近,谁就是冠军;谁差得最远,谁就是菜鸟。本题给出一系列弹洞的平面坐标(x,y),请你编写程序找出冠军和菜鸟。我们假设靶心在原点(0,0)。 输入格式:…...

模型工作流:自动化的模型内部三角面剔除

1. 关于自动减面 1.1 自动减面的重要性及现状 三维模型是游戏、三维家居设计、数字孪生、VR/AR等几乎所有三维软件的核心资产,模型的质量和性能从根本上决定了三维软件的画面效果和渲染性能。其中,模型减面工作是同时关乎质量和性能这两个要素的重要工…...

力扣题目解析--两数相除

题目 给你两个整数,被除数 dividend 和除数 divisor。将两数相除,要求 不使用 乘法、除法和取余运算。 整数除法应该向零截断,也就是截去(truncate)其小数部分。例如,8.345 将被截断为 8 ,-2.…...

Python PyMupdf 去除PDF文档中Watermark标识水印

通过PDF阅读或编辑工具,可在PDF中加入Watermark标识的PDF水印,如下图: 该类水印特点 这类型的水印,会在文件的字节流中出现/Watermark、EMC等标识,那么,我们可以通过改变文件字节内容,清理掉…...

嵌入式AI STM32部署卷积神经网络的魔法棒

基于STM32部署卷积神经网络控制设备方案-AI项目-STM32部署卷积神经网络方案-红外信号复制方案-轨迹识别 项目包含下述内容 硬件部分、PCB制板、BOM表文件等等 (Hardware)外壳、3D打印文件 (3D_print)软件程序、用于电子法棒的软件程序 AI Keil等等(Software)QT上位机动作识别…...

电路元件与电路基本定理

电流、电压和电功率 电流 1 定义: 带电质点的有序运动形成电流 。 单位时间内通过导体横截面的电量定义为电流强度, 简称电流,用符号 i 表示,其数学表达式为:(i单位:安培(A&#x…...

【蓝桥杯——物联网设计与开发】系列前言

前言 本系列博客是博主为准备2024年第十五届蓝桥杯大赛物联网设计与开发赛道而写,经过4个月学习备战,最终获得全国一等奖。 从第十六届蓝桥杯大赛开始,物联网赛道更换竞赛实训平台。之前的博客,可以借鉴代码思想,但引脚…...

Linux -- 从抢票逻辑理解线程互斥

目录 抢票逻辑代码: thread.hpp thread.cc 运行结果: 为什么票会抢为负数? 概念前言 临界资源 临界区 原子性 数据不一致 为什么数据不一致? 互斥 概念 pthread_mutex_init(初始化互斥锁) p…...

免费干净!付费软件的平替款!

今天给大家介绍一个非常好用的电脑录屏软件,完全没有广告界面,非常的干净简洁。 电脑录屏 无广告的录屏软件 这个软件不需要安装,打开就能看到界面直接使用了。 软件可以全屏录制,也可以自定义尺寸进行录制。 录制的声音选择也非…...

Mybatis插件better-mybatis-generator的下载与使用

1.下载 找到设置 插件 搜索better-mybatis-generator 下载并且重启IDEA 2.连接数据库 点击测试连接 连接成功如下图 3.使用插件 选择对应的表 右击选择 注意:mysql8.0驱动一定要勾上mysql_8 其他地方不要动 然后实体类 mapper xml就都生成好了 mapper里有默认增删…...

【测试】接口测试

长期更新好文,建议关注收藏! 目录 接口规范接口测试用例设计postmanRequests 复习HTTP超文本传输协议 复习cookiesession 实现方式 1.工具 如postman ,JMeter(后者功能更全) 2.代码 pythonrequests / javahttpclient【高级】 接…...

靶机系列|VULNHUB|DC-2

免责声明: 笔记只是方便各位师傅学习知识,以下代码、网站只涉及学习内容,其他的都与本人无关,切莫逾越法律红线,否则后果自负。 泷羽sec官网:https://longyusec.com/ 泷羽sec B站地址:https://s…...

上手教程:使用Terraform打造弹性VPC架构

最近Akamai发布的虚拟专用云(VPC)功能提供了一种隔离的网络,让云资源可以用私密的方式进行通信。 关于Akamai VPC功能,最棒的地方在于它有着极高的灵活性。用户可以通过Cloud Manager、开发人员工具(如CLI&#xff09…...

详解VHDL如何编写Testbench

1.概述 仿真测试平台文件(Testbench)是可以用来验证所设计的硬件模型正确性的 VHDL模型,它为所测试的元件提供了激励信号,可以以波形的方式显示仿真结果或把测试结果存储到文件中。这里所说的激励信号可以直接集成在测试平台文件中,也可以从…...

Kafka为什么要放弃Zookeeper

1.Kafka简介 Apache Kafka最早是由Linkedin公司开发,后来捐献给了Apack基金会。 Kafka被官方定义为分布式流式处理平台,因为具备高吞吐、可持久化、可水平扩展等特性而被广泛使用。目前Kafka具体如下功能: 消息队列,Kafka具有系统解耦、流…...

python 渗透开发工具之SQLMapApi Server不同IP服务启动方式处理 解决方案SqlMapApiServer外网不能访问的情况

目录 说在前面 什么是 SQLMapAPI 说明 sqlmapApi能干什么 sqlmapApi 服务安装相关 kali-sqlmap存放位置 正常启动sqlmap-api server SqlMapApi-Server 解决外网不能访问情况 说在前面 什么是sqlmap 这个在前面已经说过了,如果这个不知道,就可以…...

go语言的成神之路-筑基篇-gin常用功能

第一节-gin参数绑定 目录 第一节-?gin参数绑定 ShouldBind简要概述 功能: 使用场景: 可能的错误: 实例代码 效果展示 第二节-gin文件上传 选择要上传的文件 选择要上传的文件。 效果展示? 代码部分 第三节-gin请求重定向 第…...

K8S中,pod的创建流程

kubelet创建pod流程 流程图 OCI(Open Container Initiative)是一个由docker社区发起的项目,Docker、containerd CNI(Container Network Interface)网络配置:为容器分配IP地址、配置网络接口、设置路由 C…...

Windows系统提示synsoacc.dll文件报错要怎么解决?

一、文件丢失问题:深度剖析与应对策略 文件丢失是电脑运行时常见的问题之一。它可能由多种原因引起,如硬盘故障、病毒攻击、不当的文件操作等。当Windows系统提示synsoacc.dll丢失时,通常意味着该文件对于当前正在运行的程序或系统服务至关重…...

【从0带做】基于Springboot3+Vue3的高校食堂点餐系统

大家好,我是武哥,最近给大家手撸了一个基于SpringBoot3Vue3的高校食堂点餐系统,可用于毕业设计、课程设计、练手学习,系统全部原创,如有遇到网上抄袭站长的,欢迎联系博主~ 项目演示视频和教程视频 https:…...

C语言-基因序列转换独热码(one-hot code)

1.题目要求 (语言: C)在生物信息学家处理基因序列时,经常需要将基因序列转化为独热码,在英文文献中称做 one-hot code, 直观来说就是有多少个状态就有多少比特,而且只有一个比特为1,其他全为0的一种码制。 如基因序列有四种状态&…...

git在idea中操作频繁出现让输入token或用户密码,可以使用凭证助手(使用git命令时输入的用户密码即可) use credential helper

1、打开 idea 设置,找到 git 路径 File | Settings | Version Control | Git 2、勾选 Use credential helper 即可...

《机器视觉:开启智能新时代》

《机器视觉:开启智能新时代》 一、机器视觉:工业之眼的崛起二、核心组件:构建精准视觉系统(一)光源:照亮视界的画笔(二)镜头:聚焦精准的慧眼(三)相…...

C#冒泡排序

一、冒泡排序基本原理 冒泡排序是一种简单的排序算法。它重复地走访要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。 以一个简单的整数数…...

计算机网络习题(第1章 概论 第2章 数据通信基础)

第1章 概论 1、计算机网络 2、互联网 3、计算机网络体系结构 分层模型 OSI/RM 7层模型 TCP/IP 5层模型 协议、PDU、SDU、SAP等术语 数据封装(计算) 第2章 数据通信基础 1、数据通信系统组成 2、主要性能指标 数据传输速率 码元速率 时延 3…...

从0入门自主空中机器人-4-【PX4与Gazebo入门】

前言: 从上一篇的文章 从0入门自主空中机器人-3-【环境与常用软件安装】 | MGodmonkeyの世界 中我们的机载电脑已经安装了系统和常用的软件,这一篇文章中我们入门一下无人机常用的开源飞控PX4,以及ROS中无人机的仿真 1. PX4的安装 1.1 PX4固件代码的下载…...

百度热力图数据日期如何选择

目录 1、看日历2、看天气 根据研究内容定,一般如果研究城市活力的话,通常会写“非重大节假日,非重大活动,非极端天气等”。南方晴天不多,有小雨或者中雨都可认为没有影响,要不然在南方很难找到完全一周没有…...