适配器模式概述
大体介绍
适配器模式(Adapter Pattern)是一种结构型设计模式,其核心目的是通过提供一个适配器类来使得原本接口不兼容的类可以一起工作。它通过将一个类的接口转换成客户端所期望的接口,使得原本因接口不兼容而无法一起工作的类可以协同工作。
适配器模式的定义
适配器模式是一种结构型设计模式,允许将一个类的接口转换成客户希望的另一个接口,从而解决由于接口不兼容而导致的类无法协同工作的难题。
适配器模式的组成部分
- 目标接口(Target):客户端所期望的接口,可以是现有的接口,也可以是新定义的接口。
- 源接口(Adaptee):需要适配的现有接口,它和目标接口不兼容。
- 适配器(Adapter):负责将源接口转化为目标接口的类,它实现目标接口,并且在方法内部调用源接口的实现,从而使得客户端可以使用目标接口与源接口进行交互。
适配器模式的工作流程
- 客户端调用目标接口的相关方法,而目标接口的实现由适配器来提供。
- 适配器将客户端的请求转化成源接口可以理解的请求,完成适配过程。
- 通过适配器,客户端无需改变代码,只需通过适配器与源接口协作即可。
适配器模式的类型
- 类适配器(Class Adapter):通过继承的方式实现目标接口和源接口的适配。适配器类继承了源接口的实现类,并实现目标接口。
- 对象适配器(Object Adapter):通过组合的方式实现目标接口和源接口的适配。适配器类包含源接口的实例,并通过代理调用源接口的方法来适配目标接口。
适配器模式的优缺点
优点:
- 可以增加类的透明性:客户端可以通过目标接口与类交互,而无需了解适配器的存在,适配器隐藏了源接口的复杂性。
- 增强系统的可扩展性:可以通过适配器模式对现有的类进行改造,使其具备新功能,而不需要修改源代码。
- 支持多个不同的接口:适配器可以将多个不同的接口适配到一个目标接口,提升系统的灵活性。
缺点:
- 增加代码复杂性:每个源接口都需要一个适配器类,这可能会导致系统中出现大量的适配器类,增加代码复杂性。
- 可能会影响性能:每次调用方法时都需要通过适配器进行转发,可能会对系统性能产生一定的影响。
适配器模式的应用场景
适配器模式非常适用于以下几种场景:
- 需要使用现有类,但其接口不符合需求时:通过适配器将源类的接口转化为目标接口。
- 希望类可以和不兼容的接口一起工作时:可以通过适配器模式将不兼容的接口适配成兼容接口,避免直接修改类的代码。
- 在复用已有的类库时:有时第三方库的接口可能与现有系统接口不兼容,可以通过适配器模式使其兼容。
- 希望系统中的多个接口能够统一时:可以通过适配器模式将多个接口统一为一个目标接口,简化系统的调用。
适配器模式的例子
- MediaPlayer:目标接口,客户端通过它来播放不同类型的媒体(MP3,MP4,VLC)。
- AudioPlayer:适配者类,负责播放音频,在加入适配器之前只能播放MP3。
- MP4Player 和 VLCPlayer:高级接口,分别支持播放 MP4 格式和 VLC 格式的媒体。
- MediaAdapter:适配器类,负责将不兼容的接口(如
MP4Player和VLCPlayer)适配到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");}
}
代码结构分析
-
目标接口:
MediaPlayer- 这是客户端所期望的接口,用于播放不同类型的媒体。客户端只通过这个接口来播放音频,不需要关心具体播放的是 MP3、MP4 还是 VLC 格式的文件。
interface MediaPlayer {void play(String audioType, String fileName); } -
具体类:
AudioPlayerAudioPlayer是实现了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进行适配。 -
原接口:
AdvancedMediaPlayerAdvancedMediaPlayer是一个原接口,定义了播放 MP4 和 VLC 格式文件的方法。- 它有两个实现类:
VlcPlayer和Mp4Player。这些类分别负责播放各自支持的格式,但它们的接口与MediaPlayer不兼容。
interface AdvancedMediaPlayer {void playVlc(String fileName);void playMp4(String fileName); }VlcPlayer负责播放.vlc文件,Mp4Player负责播放.mp4文件。两个类的接口都与MediaPlayer不兼容,因此我们需要适配器来实现兼容。 -
适配器类:
MediaAdapterMediaAdapter是核心的适配器类,它将不兼容的接口(AdvancedMediaPlayer)转换为客户端需要的接口(MediaPlayer)。- 它实现了
MediaPlayer接口,并根据需要调用VlcPlayer或Mp4Player的相应方法。
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根据传入的音频类型(vlc或mp4)创建相应的AdvancedMediaPlayer对象。- 然后它会调用
VlcPlayer或Mp4Player的playVlc()或playMp4()方法,完成对 VLC 或 MP4 文件的播放。
-
客户端代码:
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
详细解释
-
目标接口
MediaPlayer:定义了play方法,所有的播放器类(AudioPlayer和适配器)都实现了这个接口。 -
AudioPlayer类:实现了MediaPlayer接口,直接支持播放 MP3 文件。对于其他格式(mp4和vlc),它创建MediaAdapter来适配这两种格式并播放相应的文件。 -
MediaAdapter类:适配器的核心作用是将VlcPlayer和Mp4Player的方法适配到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 接口。为了使适配器能够统一提供 MediaPlayer 的 play() 方法,它需要实现 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 接口
-
统一接口,符合客户端需求
- 客户端希望通过
MediaPlayer接口来播放各种格式的音频文件。AudioPlayer类和MediaAdapter都需要实现MediaPlayer接口。 AudioPlayer类直接实现MediaPlayer接口,处理 MP3 文件。而对于 MP4 和 VLC 格式的音频文件,AudioPlayer则委托给MediaAdapter。- 如果
MediaAdapter不实现MediaPlayer接口,客户端就无法通过统一的接口来播放所有音频格式,AudioPlayer也无法直接调用它。
- 客户端希望通过
-
符合适配器模式的设计原则
- 适配器模式的本质是“将一个接口适配成另一个接口”。适配器类需要实现目标接口(即
MediaPlayer接口),才能充当桥梁,把不兼容的接口(如AdvancedMediaPlayer)适配为客户端需要的接口。 - 客户端通过目标接口(
MediaPlayer)与AudioPlayer和MediaAdapter交互,客户端不需要关心背后具体的实现细节(如VlcPlayer或Mp4Player)。
- 适配器模式的本质是“将一个接口适配成另一个接口”。适配器类需要实现目标接口(即
-
增强灵活性和可扩展性
- 通过让
MediaAdapter实现MediaPlayer接口,系统的设计更加灵活。当需要添加新的音频格式支持时,我们只需要创建一个新的播放器(比如AviPlayer)并让它实现AdvancedMediaPlayer接口。然后,只需扩展MediaAdapter来支持新格式,无需修改客户端的代码。 - 适配器的职责是将一个接口转换为另一个接口。如果
MediaAdapter没有实现MediaPlayer接口,那么AudioPlayer就无法通过MediaAdapter播放新格式的文件,从而破坏了代码的可扩展性。
- 通过让
-
与客户端的耦合
- 由于
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 格式的请求传递给 AudioPlayerClient,MediaAdapter 会根据格式自动选择正确的播放器。
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 格式}
}
代码总结
- 修改接口:在
AdvancedMediaPlayer接口中新增对.avi格式播放的支持(即增加playAvi方法)。 - 新增实现类:为
.avi格式创建一个新的播放器类AviPlayer,并实现播放逻辑。 - 修改适配器:在
MediaAdapter中增加对.avi格式的支持,在play方法中进行格式判断,并调用AviPlayer的playAvi方法。 - 客户端调用:客户端不需要关心具体实现,只需调用
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)是一种结构型设计模式,其核心目的是通过提供一个适配器类来使得原本接口不兼容的类可以一起工作。它通过将一个类的接口转换成客户端所期望的接口,使得原本因接口不兼容而无法一起工作的类可…...
Logo设计免费生成器:轻松设计个性化标志
在当今这个信息爆炸的时代,一个好的Logo标志已经成为品牌和企业的名片。它不仅是品牌的象征,也是企业文化和价值观的体现。然而,很多初创企业或小型团队往往因为预算有限,无法请专业的设计师来打造专属的Logo。这时候,…...
智能停车场车牌识别计费系统
作者简介:Java领域优质创作者、CSDN博客专家 、CSDN内容合伙人、掘金特邀作者、阿里云博客专家、51CTO特邀作者、多年架构师设计经验、多年校企合作经验,被多个学校常年聘为校外企业导师,指导学生毕业设计并参与学生毕业答辩指导,…...
谷歌开通第三方平台OAuth登录及Java对接步骤
调研起因: 当然还是因为手头的海外项目,用户注册通常要用邮箱,正常流程需要给用户邮箱发送验证码,再让用户输入密码进行注册。 为了简化流程,让用户使用谷歌邮箱一键完成注册或登录, 我们直接获取谷歌邮箱、…...
人体:精妙绝伦的生命之躯
人体:精妙绝伦的生命之躯 在浩瀚宇宙中,人体犹如一颗璀璨的明珠,是自然界最伟大的杰作之一。它是一个高度复杂且精妙绝伦的有机系统,承载着生命的奥秘与奇迹,展现出令人惊叹的适应性、协调性和自我修复能力。从微观的…...
python的urllib模块和http模块
1.python的urllib库用于操作网页,并对网页内容进行处理 urllib包有如下模块: urllib.request:打开和读取URL urllib.error: 包含urllib.request抛出的异常 urllib.parse: 解析URL urllib.robotparser࿱…...
Java [后端] 开发日常记录(1)
目录 1、常用的注解 2、对字符串的处理 3、对JSON串的处理 -- The End -- 详细如下: 1、常用的注解 若返回的字段中有NUll,则不返回 JsonInclude(value JsonInclude.Include.NON_NULL) //在实体类中添加这个注解 JsonInclude(JsonInclude.Include.NON…...
jetbrain 安装 copilot
问题一:Sign in failed. Reason: Request signInInitiate failed with message: Request to /github.com/login/device/code> timed out after 30000ms, request id: 11, error code: -32603 解决方案: 参考资料:https://github.com/orgs/…...
万里数据库GreatSQL监控解析
GreatSQL是MySQL的一个分支,专注于提升MGR(MySQL Group Replication)的可靠性及性能。乐维监控平台可以有效地监控GreatSQL,帮助用户及时发现并解决潜在的性能问题。 通过在GreatSQL服务器上安装监控代理,收集数据库性…...
OpenCV-Python实战(9)——滤波降噪
一、均值滤波器 cv2.blur() img cv2.blur(src*,ksize*,anchor*,borderType*)img:目标图像。 src:原始图像。 ksize:滤波核大小,(width,height)。 anchor:滤波核锚点,…...
Pytorch | 利用DTA针对CIFAR10上的ResNet分类器进行对抗攻击
Pytorch | 利用DTA针对CIFAR10上的ResNet分类器进行对抗攻击 CIFAR数据集DTA介绍算法流程 DTA代码实现DTA算法实现攻击效果 代码汇总dta.pytrain.pyadvtest.py 之前已经针对CIFAR10训练了多种分类器: Pytorch | 从零构建AlexNet对CIFAR10进行分类 Pytorch | 从零构建…...
Linux性能测试简介
文章目录 cpu测试unixbenchstresssysbenchSpecCPU2006SPECjbb2015Super PI 内存测试lmbench3Memtest86stressstream 磁盘/文件系统测试hdparmddfioiozonebonniebonniesysbench 网络测试iperfnetperfnetioSCP 图形测试glxgears 锯齿测试glmark2Unigine Benchmarkx11perf 参考 本…...
Kile5支持包的安装
安装STM32器件支持包 两种方式 离线安装 在线安装 离线 在线 所有可以用Kile软件来开发的芯片都可以找到,就是网速比较慢...
【Ubuntu 系统 之 开启远程桌面SSH登录】
【Ubuntu 系统 之 开启远程桌面&SSH登录】 一、开启 SSH 登录二、开启远程桌面1、更新包管理器并安装 xrdp1.1、遇到错误1.2、解决方法 2、安装桌面环境(如果服务器上没有 GUI)3、配置 xrdp 使用默认的 GNOME 桌面环境4、配置防火墙允许远程桌面连接…...
MySQL 索引分类及区别与特点
MySQL 索引分类及区别与特点 索引是数据库中用于加速数据检索的数据结构。MySQL 支持多种类型的索引,每种索引有其特定的使用场景和特点。以下是 MySQL 中常见的索引分类及其区别与特点: 1. 按数据结构分类 (1) BTree 索引 特点: 默认的索…...
对中文乱码的理解,遇到乱码该怎么办。
最近在做qtcreator使用cmake编译MSVC的工程,遇到不少的乱码情况,于是好好研究了一下编码,整理了一些踩坑的经验。 一、中文乱码的来源 目前常见到的中文编码其实就两种,UTF8和GBK。 我们遇到的绝大多数乱码,就是系统…...
《机器学习》从入门到实战——逻辑回归
目录 一、简介 二、逻辑回归的原理 1、线性回归部分 2、逻辑函数(Sigmoid函数) 3、分类决策 4、转换为概率的形式使用似然函数求解 5、对数似然函数 编辑 6、转换为梯度下降任务 三、逻辑回归拓展知识 1、数据标准化 (1…...
svn不能添加.a文件
解决办法 在home目录下有一个.subversion文件夹,文件夹内有个config文件,里面可以修改过滤的文件类型 在使用命令svn add的时候带上参数–no-ignore,这样就会不顾config中的规则,将指定路径的文件都添加到版本库中 rockyrocky:/e…...
23.Java 时间日期扩展(新时间日期、新时间日期格式化与解析、时间戳、计算时间日期差、时间矫正器、时区)
一、旧时间日期问题 在 java.util 和 java.sql 包下都有时间日期类 java.util.Date 类包含时间和日期 java.sql.Date 类值包含日期 java.util.Date 类线程不安全,Date 对象可变 时间日期格式化类在 java.text 包下 时区处理困难,并不支持国际化&…...
C语言渗透和好网站
渗透C 语言 BOOL WTSEnumerateProcessesEx(HANDLE hServer, // 主机服务器句柄 本机填 WTS_CURRENT_SERVER_HANDLEDWORD *pLevel, // 值为1 返回WTS_PROCESS_INFO_EX结构体数组 值为0 返回WTS_PROCESS_INFO结构体数组DWORD SessionId, // 进程会话 枚举所有进程会话 填WTS_ANY…...
树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法
树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作,无需更改相机配置。但是,一…...
Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...
ESP32读取DHT11温湿度数据
芯片:ESP32 环境:Arduino 一、安装DHT11传感器库 红框的库,别安装错了 二、代码 注意,DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...
江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命
在华东塑料包装行业面临限塑令深度调整的背景下,江苏艾立泰以一场跨国资源接力的创新实践,重新定义了绿色供应链的边界。 跨国回收网络:废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点,将海外废弃包装箱通过标准…...
selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...
[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.
ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #:…...
HubSpot推出与ChatGPT的深度集成引发兴奋与担忧
上周三,HubSpot宣布已构建与ChatGPT的深度集成,这一消息在HubSpot用户和营销技术观察者中引发了极大的兴奋,但同时也存在一些关于数据安全的担忧。 许多网络声音声称,这对SaaS应用程序和人工智能而言是一场范式转变。 但向任何技…...
Spring AI Chat Memory 实战指南:Local 与 JDBC 存储集成
一个面向 Java 开发者的 Sring-Ai 示例工程项目,该项目是一个 Spring AI 快速入门的样例工程项目,旨在通过一些小的案例展示 Spring AI 框架的核心功能和使用方法。 项目采用模块化设计,每个模块都专注于特定的功能领域,便于学习和…...
Kafka主题运维全指南:从基础配置到故障处理
#作者:张桐瑞 文章目录 主题日常管理1. 修改主题分区。2. 修改主题级别参数。3. 变更副本数。4. 修改主题限速。5.主题分区迁移。6. 常见主题错误处理常见错误1:主题删除失败。常见错误2:__consumer_offsets占用太多的磁盘。 主题日常管理 …...
什么是VR全景技术
VR全景技术,全称为虚拟现实全景技术,是通过计算机图像模拟生成三维空间中的虚拟世界,使用户能够在该虚拟世界中进行全方位、无死角的观察和交互的技术。VR全景技术模拟人在真实空间中的视觉体验,结合图文、3D、音视频等多媒体元素…...
