Java特性之设计模式【适配器模式】

发布于:2024-05-16 ⋅ 阅读:(49) ⋅ 点赞:(0)

一、适配器模式

概述

适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能

这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。举个真实的例子,读卡器是作为内存卡和笔记本之间的适配器。您将内存卡插入读卡器,再将读卡器插入笔记本,这样就可以通过笔记本来读取内存卡

主要解决

主要解决在软件系统中,常常要将一些"现存的对象"放到新的环境中,而新环境要求的接口是现对象不能满足的

何时使用

  • 系统需要使用现有的类,而此类的接口不符合系统的需要
  • 想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作,这些源类不一定有一致的接口
  • 通过接口转换,将一个类插入另一个类系中。(比如老虎和飞禽,现在多了一个飞虎,在不增加实体的需求下,增加一个适配器,在里面包容一个虎对象,实现飞的接口)

优缺点

优点:

  • 可以让任何两个没有关联的类一起运行
  • 提高了类的复用
  • 增加了类的透明度
  • 灵活性好

缺点:

  • 过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构
  • 由于 JAVA 至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类

1. 各个角色介绍

1.1 目标角色(Target)

  • 定义了客户端期望的接口,可以是抽象类或接口

1.2 适配器(Adapter)

  • 实现目标接口,并持有一个被适配者的实例,负责把被适配者的接口转换成目标接口

1.3 被适配者(Adaptee)

  • 包含了需要被适配的接口

2. UML图

​ 我们将创建一个 AdvancedMediaPlayer 高级播放器接口和实现了 AdvancedMediaPlayer 接口的实体类 Mp4Player (Mp4格式播放器)、VlcPlayer (Vlc格式播放器)。MediaPlayer 媒体播放器接口和实现了 MediaPlayer 接口的实体类 MediaAdapter(媒体适配器,内置高级播放器,用于根据音频类型去加载不同播放器)、AudioPlayer(音频播放器。内带有媒体适配器,用于外部调用时,能够根据内置的适配器进行内部的动作执行)

在这里插入图片描述

3. 具体例子和代码

角色分配

  • AdvancedMediaPlayer:高级播放器接口
    • Mp4Player:Mp4格式播放器
    • VlcPlayer:Vlc格式播放器
  • MediaPlayer:媒体播放器接口
    • MediaAdapter:媒体适配器
    • AudioPlayer:音频播放器

3.1 高级播放器接口及其实现类

  • AdvancedMediaPlayer
package com.vinjcent.prototype.adapter;

/**
 * @author vinjcent
 * @description 高级播放器,用于执行各种播放器
 * @since 2024/5/9 17:35
 */
public interface AdvancedMediaPlayer {

    /**
     * 可以播放vlc格式的文件
     *
     * @param fileName 文件名称
     */
    void playVlc(String fileName);

    /**
     * 可以播放mp4格式的文件
     *
     * @param fileName 文件名称
     */
    void playMp4(String fileName);

}

  • Mp4Player
package com.vinjcent.prototype.adapter;

/**
 * @author vinjcent
 * @description Mp4格式播放器
 * @since 2024/5/9 18:02
 */
public class Mp4Player implements AdvancedMediaPlayer {

    @Override
    public void playVlc(String fileName) {
        // 不做处理...
    }

    @Override
    public void playMp4(String fileName) {
        System.out.println("Playing mp4 file. Name: " + fileName);
    }

}

  • VlcPlayer
package com.vinjcent.prototype.adapter;

/**
 * @author vinjcent
 * @description Vlc格式播放器
 * @since 2024/5/9 18:00
 */
public class VlcPlayer implements AdvancedMediaPlayer {

    @Override
    public void playVlc(String fileName) {
        System.out.println("Playing vlc file. Name: " + fileName);
    }

    @Override
    public void playMp4(String fileName) {
        // 不做处理...
    }

}

3.2 媒体播放器接口及其实现类

  • MediaPlayer
package com.vinjcent.prototype.adapter;

/**
 * @author vinjcent
 * @description 媒体播放器接口,根据不同音频类型创建并执行播放器
 * @since 2024/5/9 17:32
 */
public interface MediaPlayer {

    /**
     * 根据音频类型执行播放动作
     *
     * @param audioType 音频类型
     * @param fileName  文件名称
     */
    void play(String audioType, String fileName);

}

  • MediaAdapter
package com.vinjcent.prototype.adapter;

import io.swagger.annotations.ApiModelProperty;

/**
 * @author vinjcent
 * @description 媒体适配器,内置高级播放器,用于根据音频类型去加载不同播放器
 * @since 2024/5/9 18:02
 */
public class MediaAdapter implements MediaPlayer {

    @ApiModelProperty("播放器")
    private AdvancedMediaPlayer advancedMusicPlayer;


    public MediaAdapter(String audioType) {
        if ("vlc".equalsIgnoreCase(audioType)) {
            advancedMusicPlayer = new VlcPlayer();
        } else if ("mp4".equalsIgnoreCase(audioType)) {
            advancedMusicPlayer = new Mp4Player();
        }
    }

    @Override
    public void play(String audioType, String fileName) {
        if ("vlc".equalsIgnoreCase(audioType)) {
            advancedMusicPlayer.playVlc(fileName);
        } else if ("mp4".equalsIgnoreCase(audioType)) {
            advancedMusicPlayer.playMp4(fileName);
        }
    }

}
  • AudioPlayer
package com.vinjcent.prototype.adapter;

import io.swagger.annotations.ApiModelProperty;

/**
 * @author vinjcent
 * @description 音频播放器。内带有媒体适配器,用于外部调用时,能够根据内置的适配器进行内部的动作执行
 * @since 2024/5/9 19:21
 */
public class AudioPlayer implements MediaPlayer {

    @ApiModelProperty("媒体适配器")
    private MediaAdapter mediaAdapter;

    @Override
    public void play(String audioType, String fileName) {
        // 播放 mp3 音乐文件的内置支持
        if ("mp3".equalsIgnoreCase(audioType)) {
            System.out.println("Playing mp3 file. Name: " + fileName);
        }
        // mediaAdapter 提供了播放其他文件格式的支持
        else if ("vlc".equalsIgnoreCase(audioType)
                || "mp4".equalsIgnoreCase(audioType)) {
            mediaAdapter = new MediaAdapter(audioType);
            mediaAdapter.play(audioType, fileName);
        } else {
            System.out.println("Invalid media. " +
                    audioType + " format not supported");
        }
    }

}

3.3 测试主函数

package com.vinjcent.prototype.adapter;

/**
 * @author vinjcent
 * @description 适配器模式
 * @since 2024/5/9 19:51
 */
public class Main {


    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");
    }

}

  • 测试结果

在这里插入图片描述

4. 使用场景

  • 有动机地修改一个正常运行的系统的接口,这时应该考虑使用适配器模式

在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到