设计模式 六月 03, 2021

适配器模式

文章字数 7k 阅读约需 6 mins. 阅读次数 0

适配器模式

解决问题

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

方案

把一个类的接口变换成客户端所期待的另一种接口, Adapter模式使原本因接口不匹配(或者不兼容)而无法在一起工作的两个类能够在一起工作.

结构

  • 目标角色(Target):— 定义Client使用的与特定领域相关的接口。
  • 客户角色(Client):与符合Target接口的对象协同。
  • 被适配角色(Adaptee):定义一个已经存在并已经使用的接口,这个接口需要适配。
  • 适配器角色(Adapter) :适配器模式的核心。它将对被适配Adaptee角色已有的接口转换为目标角色Target匹配的接口。对Adaptee的接口与Target接口进行适配.

类适配器

适配器继承需要适配的类

适配器模式-类适配器

  • 用一个具体的Adapter类对Adaptee和Target进行匹配。结果是当我们想要匹配一个类以及所有它的子类时,类Adapter将不能胜任工作。
  • 使得Adapter可以重定义Adaptee的部分行为,因为Adapter是Adaptee的一个子类。
  • 仅仅引入了一个对象,并不需要额外的指针以间接得到 Adaptee。

通用代码

// 已存在的、具有特殊功能、但不符合我们既有的标准接口的类
class Adaptee {
    public void specificRequest() {
        System.out.println("被适配类 具有特殊功能...");
    }
}

// 目标接口,或称为标准接口
interface Target {
    public void request();
}

// 具体目标类,只提供普通功能
/*
class ConcreteTarget implements Target {
    public void request() {
        System.out.println("普通类 具有普通功能...");
    }
}
*/

// 适配器类,继承了被适配类,同时实现标准接口
class Adapter extends Adaptee implements Target{
    public void request() {
        super.specificRequest();
    }
}

// 测试类
public class Client {
    public static void main(String[] args) {
        // 使用普通功能类
       /* Target concreteTarget = new ConcreteTarget();//实例化一个普通类
        concreteTarget.request();
       */
       // 使用特殊功能类,即适配类
        Target adapter = new Adapter();
        adapter.request();
    }
}

对象适配器

适配器容纳一个它包裹的类的实例。

适配器模式-对象适配器

  • 允许一个Adapter与多个Adaptee—即Adaptee本身以及它的所有子类(如果有子类的话)—同时工作。Adapter也可以一次给所有的Adaptee添加功能。
  • 使得重定义Adaptee的行为比较困难。这就需要生成Adaptee的子类并且使得Adapter引用这个子类而不是引用Adaptee本身。

通用代码

//关键代码

// 适配器类,直接关联被适配类,同时实现标准接口
class Adapter implements Target{
    // 直接关联被适配类
    private Adaptee adaptee;

    // 可以通过构造函数传入具体需要适配的被适配类对象
    public Adapter (Adaptee adaptee) {
        this.adaptee = adaptee;
    }

    public void request() {
        // 这里是使用委托的方式完成特殊功能
        this.adaptee.specificRequest();
    }
}

// 测试类
public class Client {
    public static void main(String[] args) {
        // 使用普通功能类
        Target concreteTarget = new ConcreteTarget();
        concreteTarget.request();

        // 使用特殊功能类,即适配类,
        // 需要先创建一个被适配类的对象作为参数
        Target adapter = new Adapter(new Adaptee());
        adapter.request();
    }
}

适用性

  • 使用一个已经存在的类,而它的接口不符合需求
  • 创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作。
  • 使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。即仅仅引入一个对象,并不需要额外的指针以间接取得adaptee。

优缺点

优点

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

缺点

过多使用,会使系统非常凌乱,不易维护.

经典实现

音乐播放器.

适配器模式-音乐播放器

// 目标接口
public interface MediaPlayer {
   public void play(String audioType, String fileName);
}

// 目标抽象类\接口
public interface AdvancedMediaPlayer { 
   public void playVlc(String fileName);
   public void playMp4(String fileName);
}

// 目标具体类
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) {
      //什么也不做
   }
}

// 适配器类 -- 对象适配器
public 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();
      }  
   }
 
   @Override
   public void play(String audioType, String fileName) {
      if(audioType.equalsIgnoreCase("vlc")){
         advancedMusicPlayer.playVlc(fileName);
      }else if(audioType.equalsIgnoreCase("mp4")){
         advancedMusicPlayer.playMp4(fileName);
      }
   }
}

// 目标类
public class AudioPlayer implements MediaPlayer {
   MediaAdapter mediaAdapter; 
 
   @Override
   public void play(String audioType, String fileName) {    
 
      //播放 mp3 音乐文件的内置支持
      if(audioType.equalsIgnoreCase("mp3")){
         System.out.println("Playing mp3 file. Name: "+ fileName);         
      } 
      //mediaAdapter 提供了播放其他文件格式的支持
      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 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");
   }
}

框架中应用

springMVC,

数据库连接jdbc.

日记记录sl4j.

0%