23种设计模式介绍:https://mp.csdn.net/postedit/90552052
在现实生活中,经常出现两个对象因接口不兼容而不能在一起工作的实例,这时需要第三者进行适配。例如,讲中文的人同讲英文的人对话时需要一个翻译,用直流电的笔记本电脑接交流电源时需要一个电源适配器,用计算机访问照相机的 SD 内存卡时需要一个读卡器等。
1.1 意图
- 在软件开发中采用类似于电源适配器的设计和编码技巧被称为适配器模式。
- 通常情况下,客户端可以通过目标类的接口访问它所提供的服务。有时,现有的类可以满足客户类的功能需要,但是它所提供的接口不一定是客户类所期望的,这可能是因为现有类中方法名与目标类中定义的方法名不一致等原因所导致的。
- 在这种情况下,现有的接口需要转化为客户类期望的接口,这样保证了对现有类的重用。如果不进行这样的转化,客户类就不能利用现有类所提供的功能,适配器模式可以完成这样的转化。
- 在适配器模式中可以定义一个包装类,包装不兼容接口的对象,这个包装类指的就是适配器(Adapter),它所包装的对象就是适配者(Adaptee),即被适配的类。
- 适配器提供客户类需要的接口,适配器的实现就是把客户类的请求转化为对适配者的相应接口的调用。也就是说:当客户类调用适配器的方法时,在适配器类的内部将调用适配者类的方法,而这个过程对客户类是透明的,客户类并不直接访问适配者类。因此,适配器可以使由于接口不兼容而不能交互的类可以一起工作。这就是适配器模式的模式动机。
1.2 模式定义
适配器模式(Adapter Pattern) :将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。
1.3 使用场景
1、系统需要使用现有的类,而此类的接口不符合系统的需要。 2、想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作,这些源类不一定有一致的接口。 3、通过接口转换,将一个类插入另一个类系中。(比如老虎和飞禽,现在多了一个飞虎,在不增加实体的需求下,增加一个适配器,在里面包容一个虎对象,实现飞的接口。)有动机地修改一个正常运行的系统的接口,这时应该考虑使用适配器模式。
1.4 模式结构
注意事项:适配器不是在详细设计时添加的,而是解决正在服役的项目的问题。
适配器模式包含如下角色:
- Target:目标抽象类
- Adapter:适配器类
- Adaptee:适配者类
- Client:客户类
适配器模式有对象适配器和类适配器两种实现:
对象适配器:
package DesignPattern23.Adapter;
/**
* Description:
* Author:
* Date: 2019/11/11
*/
public class ClassAdaper {
public static void main(String[] args) {
System.out.println("类适配器模式测试:");
Adaptee adaptee = new Adaptee();
Target target = new ClassAdapter(adaptee);
target.request();
}
}
//目标接口
interface Target{
public void request();
}
//适配者
class Adaptee{
public void specificRequest() {
System.out.println("适配者中的业务代码被调用!");
}
}
//类适配器类
class ClassAdapter implements Target{
private Adaptee adaptee;
public ClassAdapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
adaptee.specificRequest();
}
}
类适配器:
package DesignPattern23.Adapter;
/**
* Description:
* Author:
* Date: 2019/11/11
*/
public class ClassAdaper {
public static void main(String[] args) {
System.out.println("类适配器模式测试:");
Target target = new ClassAdapter();
target.request();
}
}
//目标接口
interface Target{
public void request();
}
//适配者
class Adaptee{
public void specificRequest() {
System.out.println("适配者中的业务代码被调用!");
}
}
//类适配器类
class ClassAdapter extends Adaptee implements Target{
@Override
public void request() {
new ClassAdapter().specificRequest();
}
}
1.5 时序图
1.6 demo
我们有一个 MediaPlayer 接口和一个实现了 MediaPlayer 接口的实体类 AudioPlayer。默认情况下,AudioPlayer 可以播放 mp3 格式的音频文件。我们还有另一个接口 AdvancedMediaPlayer 和实现了 AdvancedMediaPlayer 接口的实体类。该类可以播放 vlc 和 mp4 格式的文件。我们想要让 AudioPlayer 播放其他格式的音频文件。为了实现这个功能,我们需要创建一个实现了 MediaPlayer 接口的适配器类 MediaAdapter,并使用 AdvancedMediaPlayer 对象来播放所需的格式。AudioPlayer 使用适配器类 MediaAdapter 传递所需的音频类型,不需要知道能播放所需格式音频的实际类。AdapterPatternDemo,我们的演示类使用 AudioPlayer 类来播放各种格式。
步骤1:创建MediaPlayer接口和实现类AudioPlayer.java
package DesignPattern23.Adapter;
/**
* Description:
* Author:
* Date: 2019/11/11
*/
public interface MediaPlayer {
//仅支持MP3格式
public void play(String audioType,String fileName);
}
package DesignPattern23.Adapter;
/**
* Description:
* Author:
* Date: 2019/11/11
*/
public class AudioPlayer implements MediaPlayer{
@Override
public void play(String audioType, String fileName) {
if (audioType.equalsIgnoreCase("mp3")){
System.out.println("Playing" + audioType + "格式音乐,名字为:"+fileName);
}else {
System.out.println("格式不支持");
}
}
}
package DesignPattern23.Adapter;
/**
* Description:
* Author:
* Date: 2019/11/11
*/
public class AdapterPatternDemo {
public static void main(String[] args) {
AudioPlayer audioPlayer = new AudioPlayer();
audioPlayer.play("mp3","my heart will go on");
audioPlayer.play("mp4","my heart will go on");
}
}
执行结果为:
Playingmp3格式音乐,名字为:my heart will go on
格式不支持
Process finished with exit code 0
步骤2:增加适配器使其支持其他格式音乐。创建AdvancedMediaPlayer接口和实现类VlcPlayer,Mp4Player用于播放vlc,mp4格式。
package DesignPattern23.Adapter;
/**
* Description:
* Author:
* Date: 2019/11/11
*/
public interface AdvancedMediaPlayer {
public void play(String audioType,String fileName);
}
package DesignPattern23.Adapter;
/**
* Description:
* Author:
* Date: 2019/11/11
*/
public class Mp4Player implements AdvancedMediaPlayer{
@Override
public void play(String audioType,String fileName) {
System.out.println("Playing mp4 file. Name: "+ fileName);
}
}
package DesignPattern23.Adapter;
/**
* Description:
* Author:
* Date: 2019/11/11
*/
public class VlcPlayer implements AdvancedMediaPlayer{
@Override
public void play(String audioType,String fileName) {
System.out.println("Playing vlc file. Name: "+ fileName);
}
}
步骤3:创建适配器,实现MediaPlayer接口,用对象适配器。
package DesignPattern23.Adapter;
/**
* Description:
* Author:
* Date: 2019/11/11
*/
public class MediaAdapter implements MediaPlayer{
private AdvancedMediaPlayer advancedMediaPlayer;
public MediaAdapter(AdvancedMediaPlayer advancedMediaPlayer) {
this.advancedMediaPlayer = advancedMediaPlayer;
}
@Override
public void play(String audioType,String fileName) {
advancedMediaPlayer.play(audioType,fileName);
}
}
步骤4:修改AudioPlayer类,增加类型判断
package DesignPattern23.Adapter;
/**
* Description:
* Author:
* Date: 2019/11/11
*/
public class AudioPlayer implements MediaPlayer{
@Override
public void play(String audioType, String fileName) {
if (audioType.equalsIgnoreCase("mp3")){
System.out.println("Playing" + audioType + "格式音乐,名字为:"+fileName);
}else if (audioType.equalsIgnoreCase("mp4")){
AdvancedMediaPlayer mediaPlayer = new Mp4Player();
mediaPlayer.play(audioType,fileName);
}else if (audioType.equalsIgnoreCase("vlc")){
AdvancedMediaPlayer mediaPlayer = new VlcPlayer();
mediaPlayer.play(audioType,fileName);
} else {
System.out.println("格式不支持");
}
}
}
步骤5:test:
package DesignPattern23.Adapter;
/**
* Description:
* Author:
* Date: 2019/11/11
*/
public class AdapterPatternDemo {
public static void main(String[] args) {
AudioPlayer audioPlayer = new AudioPlayer();
audioPlayer.play("mp3","my heart will go on");
audioPlayer.play("mp4","my heart will go on");
audioPlayer.play("vlc","my heart will go on");
audioPlayer.play("mp5","my heart will go on");
}
}
执行结果为:
Playingmp3格式音乐,名字为:my heart will go on
Playing mp4 file. Name: my heart will go on
Playing vlc file. Name: my heart will go on
格式不支持
Process finished with exit code 0
17 总结
- 结构型模式描述如何将类或者对象结合在一起形成更大的结构。
- 适配器模式用于将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。
- 适配器模式包含四个角色:目标抽象类定义客户要用的特定领域的接口;适配器类可以调用另一个接口,作为一个转换器,对适配者和抽象目标类进行适配,它是适配器模式的核心;适配者类是被适配的角色,它定义了一个已经存在的接口,这个接口需要适配;在客户类中针对目标抽象类进行编程,调用在目标抽象类中定义的业务方法。
- 在类适配器模式中,适配器类实现了目标抽象类接口并继承了适配者类,并在目标抽象类的实现方法中调用所继承的适配者类的方法;在对象适配器模式中,适配器类继承了目标抽象类并定义了一个适配者类的对象实例,在所继承的目标抽象类方法中调用适配者类的相应业务方法。
- 适配器模式的主要优点是将目标类和适配者类解耦,增加了类的透明性和复用性,同时系统的灵活性和扩展性都非常好,更换适配器或者增加新的适配器都非常方便,符合“开闭原则”;类适配器模式的缺点是适配器类在很多编程语言中不能同时适配多个适配者类,对象适配器模式的缺点是很难置换适配者类的方法。
- 适配器模式适用情况包括:系统需要使用现有的类,而这些类的接口不符合系统的需要;想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类一起工作。