【设计模式】适配器模式(包装器模式),缺省适配器模式,双向适配器模式
适配器模式(Adapter Pattern)详解
一、适配器模式简介
适配器模式是一种结构型设计模式,它将一个类的接口转换成客户端所期望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以协同工作。
简单来说,适配器模式就像是一个变压器或者转接头,它可以帮助我们解决两个不兼容接口之间的合作问题。
别名为**包装器(Wrapper)**模式。
定义中所提及的接口是指广义的接口,它可以表示一个方法或者方法的集合。
二、适用场景与针对的问题
- 适用场景:当你需要使用一个已有的类,但是它的接口并不符合你的需求时;或者你需要创建一个可复用的类,该类能够与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作。
- 针对的问题:主要是解决不同接口之间的不兼容性问题,使得它们能够在不影响各自实现的情况下进行协作。
三、实际案例
想象一下,你有一台笔记本电脑和一部手机,两者都需要充电,但它们的充电插口不一样。如果你只有一个电源适配器,这个适配器能将家用电源转换为笔记本电脑所需的电压和电流类型,但对于手机则无法直接使用。此时,你就需要一个USB-C到Lightning的适配器来为你的手机充电。这里,电源适配器和USB-C到Lightning适配器就起到了适配器的作用。
3.1 适配器模式的结构与实现
适配器模式的结构(类适配器)
适配器模式的结构(对象适配器)
适配器模式的结构
适配器模式包含以下3个角色:
Target(目标抽象类)
Adapter(适配器类)
Adaptee(适配者类)
四、代码案例
假设有一个MediaPlayer
接口和其实现类AudioPlayer
,它只能播放mp3格式的音频文件。现在我们需要扩展其功能以支持更多格式如VLC和MP4,但不想改变原有的AudioPlayer
类。这时就可以使用适配器模式。
// 目标接口
interface MediaPlayer {public void play(String audioType, String fileName);
}// 已有类,实现了不同的接口
class AdvancedMediaPlayer {public void playVlc(String fileName) {System.out.println("Playing vlc file. Name: " + fileName);}public void playMp4(String fileName) {System.out.println("Playing mp4 file. Name: " + fileName);}
}// 适配器类
class MediaAdapter implements MediaPlayer {private AdvancedMediaPlayer advancedMusicPlayer;public MediaAdapter(String audioType) {if (audioType.equalsIgnoreCase("vlc")) {advancedMusicPlayer = new AdvancedMediaPlayer();} else if (audioType.equalsIgnoreCase("mp4")) {advancedMusicPlayer = new AdvancedMediaPlayer();}}@Overridepublic void play(String audioType, String fileName) {if (audioType.equalsIgnoreCase("vlc")) {advancedMusicPlayer.playVlc(fileName);} else if (audioType.equalsIgnoreCase("mp4")) {advancedMusicPlayer.playMp4(fileName);}}
}// 使用适配器的类
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("vlc") || audioType.equalsIgnoreCase("mp4")) {mediaAdapter = new MediaAdapter(audioType);mediaAdapter.play(audioType, fileName);} else {System.out.println("Invalid media. " + audioType + " format not supported");}}
}// 测试类
public class Test {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");}
}
在这个例子中,MediaAdapter
充当了适配器的角色,它将AdvancedMediaPlayer
的功能适配到了MediaPlayer
接口上,从而解决了接口不兼容的问题。通过这种方式,我们可以轻松地扩展AudioPlayer
的功能,让它支持更多的音频格式。
五、缺省适配器模式
- 定义:当不需要实现一个接口所提供的所有方法时,可先设计一个抽象类实现该接口,并为接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可以选择性地覆盖父类的某些方法来实现需求,它适用于不想使用一个接口中的所有方法的情况,又称为单接口适配器模式。
- 结构:
- 实现:
缺省适配器类的典型代码片段:
abstract class AbstractServiceClass : ServiceInterface
{public void ServiceMethod1() { } //空方法public void ServiceMethod2() { } //空方法public void ServiceMethod3() { } //空方法
}
六、双向适配器模式
- 结构:
- 实现:
public class Adapter : Target, Adaptee
{//同时维持对抽象目标类和适配者的引用private Target target;private Adaptee adaptee;public Adapter(Target target) {this.target = target;}public Adapter(Adaptee adaptee) {this.adaptee = adaptee;}public void Request() {adaptee.SpecificRequest();}public void SpecificRequest() {target.Request();}
}
七、适配器模式的优缺点与适用环境
-
模式适用环境:系统需要使用一些现有的类,而这些类的接口不符合系统的需要,甚至没有这些类的源代码
创建一个可以重复使用的类,用于和一些彼此之间没有太大关联的类,包括一些可能在将来引进的类一起工作 -
模式优点:
将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无须修改原有结构
增加了类的透明性和复用性,提高了适配者的复用性,同一个适配者类可以在多个不同的系统中复用
灵活性和扩展性非常好
类适配器模式:置换一些适配者的方法很方便
对象适配器模式:可以把多个不同的适配者适配到同一个目标,还可以适配一个适配者的子类 -
模式缺点:
类适配器模式:(1) 一次最多只能适配一个适配者类,不能同时适配多个适配者;(2) 适配者类不能为最终类;(3) 目标抽象类只能为接口,不能为类
对象适配器模式:在适配器中置换适配者类的某些方法比较麻烦
经典运用:
Sun公司在1996年公开了Java语言的数据库连接工具JDBC,JDBC使得Java语言程序能够与数据库连接,并使用SQL语言来查询和操作数据。JDBC给出一个客户端通用的抽象接口,每一个具体数据库引擎(如SQL Server、Oracle、MySQL等)的JDBC驱动软件都是一个介于JDBC接口和数据库引擎接口之间的适配器软件。抽象的JDBC接口和各个数据库引擎API之间都需要相应的适配器软件,这就是为各个不同数据库引擎准备的驱动程序。
部分内容由AI大模型生成,请注意识别!