目录

二十三种模式-适配器模式

适配器模式(Adapter Pattern)是一种结构型设计模式,它允许将不兼容的接口转换成客户端期望的接口,从而使原本因接口不匹配而不能一起工作的类可以协同工作。以下是关于适配器模式的详细介绍:

一、定义及作用

  • 定义:适配器模式将一个类的接口转换成客户端所期望的另一种接口,使原本由于接口不兼容而不能一起工作的那些类可以一起工作。
  • 作用
    • 兼容性:解决了接口不兼容的问题,让原本无法协同工作的类能够一起工作,提高了系统的兼容性。
    • 复用性:可以重用一些现有的类,而无需修改它们的代码,从而提高了代码的复用性。
    • 灵活性:客户端调用的是适配器类,而适配器类可以根据需要调用被适配类的方法,这样可以在不修改客户端代码的情况下,灵活地更换被适配类。

二、实现方式

适配器模式主要有两种实现方式:类适配器和对象适配器。

(一)类适配器
  • 结构
    • 目标接口(Target):定义客户端所期望的接口,它声明了一些方法,客户端通过这些方法来调用适配器的功能。
    • 适配器类(Adapter):通过继承被适配类来实现目标接口。它将被适配类的接口转换为目标接口,内部持有被适配类的实例,并将目标接口的方法调用转发给被适配类的实例。
    • 被适配类(Adaptee):定义了需要适配的接口,它有一些有用的方法,但是接口与客户端期望的接口不一致。
  • 示例
    • 假设有一个旧的音频播放器类AudioPlayer,它只能播放MP3格式的音频文件,接口方法为playMp3(String fileName)。现在客户端希望它也能播放VLC格式的音频文件。

    • 我们可以创建一个适配器类AudioPlayerAdapter,它继承自AudioPlayer,同时实现一个新的接口AdvancedAudioPlayer,该接口有一个方法play(String audioType, String fileName)

    • AudioPlayerAdapter类中,重写play方法,当audioType为"vlc"时,先将VLC格式的音频文件转换为MP3格式(假设有一个转换方法convertVlcToMp3),然后再调用playMp3方法来播放转换后的MP3文件。

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

      // 被适配类
      class AudioPlayer {
      public void playMp3(String fileName) {
      System.out.println("Playing mp3 file. Name: " + fileName);
      }
      }

      // 适配器类
      class AudioPlayerAdapter extends AudioPlayer implements AdvancedAudioPlayer {
      @Override
      public void play(String audioType, String fileName) {
      if (audioType.equalsIgnoreCase("vlc")) {
      String convertedFile = convertVlcToMp3(fileName);
      playMp3(convertedFile);
      }
      }

      复制代码
      private String convertVlcToMp3(String fileName) {
          // 转换逻辑,这里简化处理
          return fileName.replace(".vlc", ".mp3");
      }

      }

(二)对象适配器
  • 结构
    • 目标接口(Target):与类适配器中的目标接口相同,定义客户端期望的接口。
    • 适配器类(Adapter):实现目标接口,并且通过组合的方式包含一个被适配类的实例。适配器类将目标接口的方法调用转发给被适配类的实例。
    • 被适配类(Adaptee):与类适配器中的被适配类相同,定义了需要适配的接口。
  • 示例
    • 还是以上面的音频播放器为例。适配器类AudioPlayerAdapter实现AdvancedAudioPlayer接口,同时在类中包含一个AudioPlayer类型的成员变量。

    • AudioPlayerAdapter的构造方法中,传入一个AudioPlayer实例。在play方法中,根据audioType判断,如果是VLC格式,先调用转换方法,然后通过成员变量调用AudioPlayerplayMp3方法。

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

      // 被适配类
      class AudioPlayer {
      public void playMp3(String fileName) {
      System.out.println("Playing mp3 file. Name: " + fileName);
      }
      }

      // 适配器类
      class AudioPlayerAdapter implements AdvancedAudioPlayer {
      private AudioPlayer audioPlayer;

      复制代码
      public AudioPlayerAdapter(AudioPlayer audioPlayer) {
          this.audioPlayer = audioPlayer;
      }
      
      @Override
      public void play(String audioType, String fileName) {
          if (audioType.equalsIgnoreCase("vlc")) {
              String convertedFile = convertVlcToMp3(fileName);
              audioPlayer.playMp3(convertedFile);
          }
      }
      
      private String convertVlcToMp3(String fileName) {
          // 转换逻辑,这里简化处理
          return fileName.replace(".vlc", ".mp3");
      }

      }

三、优缺点

  • 优点
    • 提高兼容性:可以将不兼容的接口转换为目标接口,让原本不能一起工作的类能够协同工作。
    • 提高复用性:可以重用一些现有的类,无需修改它们的代码,提高了代码的复用性。
    • 符合开闭原则:客户端调用的是适配器类,而适配器类可以根据需要调用被适配类的方法,可以在不修改客户端代码的情况下,灵活地更换被适配类。
  • 缺点
    • 类适配器的缺点:由于适配器类是通过继承被适配类来实现目标接口的,所以它只能适配一个被适配类,如果要适配多个被适配类,就需要创建多个适配器类,这会增加系统的复杂性。而且,类适配器使用了继承,这可能会违反一些面向对象的设计原则,如里氏替换原则等。
    • 对象适配器的缺点:对象适配器需要在适配器类中创建被适配类的实例,这可能会增加系统的开销。而且,如果被适配类的接口有变化,适配器类也需要相应地进行修改,这可能会增加维护的难度。

四、应用场景

  • 遗留代码的适配:当需要在新的系统中使用一些遗留代码,但遗留代码的接口与新系统的接口不兼容时,可以使用适配器模式来适配这些遗留代码。
  • 第三方库的适配:当需要使用第三方库,但第三方库的接口与系统的接口不兼容时,可以使用适配器模式来适配第三方库。
  • 不同类库之间的适配:当需要将两个不同类库中的类组合在一起使用,但它们的接口不兼容时,可以使用适配器模式来适配这两个类库中的类。

案例

一、支付接口适配的背景

假设一个电商平台需要支持多种支付方式。每种支付方式都有自己的接口,例如:

  • 支付宝支付接口AlipayService,它有方法payWithAlipay(String orderId, double amount)
  • 微信支付接口WechatPayService,它有方法payWithWechat(String orderId, double amount)
  • 银联支付接口UnionPayService,它有方法payWithUnionPay(String orderId, double amount)

这些支付接口虽然功能相似,但方法名和参数等细节可能有所不同,这就导致客户端代码在调用不同的支付方式时需要编写不同的代码,增加了代码的复杂性和维护难度。

二、使用适配器模式进行适配

为了解决这个问题,可以定义一个通用的支付接口(目标接口),然后为每种支付方式创建一个适配器类,将具体的支付接口(被适配接口)适配到通用的支付接口上。

(一)定义通用支付接口(目标接口)
复制代码
public interface PaymentAdapter {
    void pay(String orderId, double amount);
}
(二)创建适配器类
  • 支付宝支付适配器

    public class AlipayAdapter implements PaymentAdapter {
    private AlipayService alipayService;

    复制代码
      public AlipayAdapter(AlipayService alipayService) {
          this.alipayService = alipayService;
      }
    
      @Override
      public void pay(String orderId, double amount) {
          alipayService.payWithAlipay(orderId, amount);
      }

    }

  • 微信支付适配器

    public class WechatPayAdapter implements PaymentAdapter {
    private WechatPayService wechatPayService;

    复制代码
      public WechatPayAdapter(WechatPayService wechatPayService) {
          this.wechatPayService = wechatPayService;
      }
    
      @Override
      public void pay(String orderId, double amount) {
          wechatPayService.payWithWechat(orderId, amount);
      }

    }

  • 银联支付适配器

    public class UnionPayAdapter implements PaymentAdapter {
    private UnionPayService unionPayService;

    复制代码
      public UnionPayAdapter(UnionPayService unionPayService) {
          this.unionPayService = unionPayService;
      }
    
      @Override
      public void pay(String orderId, double amount) {
          unionPayService.payWithUnionPay(orderId, amount);
      }

    }

(三)客户端调用

在客户端代码中,只需要使用通用的支付接口PaymentAdapter来调用支付方法,而不需要关心具体的支付方式。这样,客户端代码就可以统一地调用不同的支付方式,提高了代码的可维护性和可扩展性。

复制代码
public class PaymentClient {
    public static void main(String[] args) {
        // 假设已经创建了具体的支付服务实例
        AlipayService alipayService = new AlipayService();
        WechatPayService wechatPayService = new WechatPayService();
        UnionPayService unionPayService = new UnionPayService();

        // 创建适配器实例
        PaymentAdapter alipayAdapter = new AlipayAdapter(alipayService);
        PaymentAdapter wechatPayAdapter = new WechatPayAdapter(wechatPayService);
        PaymentAdapter unionPayAdapter = new UnionPayAdapter(unionPayService);

        // 统一调用支付方法
        alipayAdapter.pay("12345", 100.0);
        wechatPayAdapter.pay("67890", 200.0);
        unionPayAdapter.pay("11111", 300.0);
    }
}

三、适配器模式在支付接口适配中的优势

  • 统一接口:通过定义一个通用的支付接口,将不同的支付方式统一起来,客户端代码只需要关心这个通用接口,而不需要关心具体的支付实现细节。
  • 提高可维护性:当需要添加新的支付方式时,只需要创建一个新的适配器类,而不需要修改客户端代码。这符合开闭原则,提高了系统的可维护性。
  • 降低耦合度:客户端代码与具体的支付实现解耦,客户端只需要依赖通用的支付接口,而不需要直接依赖具体的支付服务类,降低了系统的耦合度。

适配器模式在处理多种支付接口的适配问题时,能够有效地解决接口不兼容的问题,提高代码的可维护性和可扩展性,是一种非常实用的设计模式。

本文是转载文章,点击查看原文
如有侵权,请联系 xyy@jishuzhan.net 删除
相关推荐
龙大大L11 分钟前
第五章:5.1 ESP32物联网应用 - MQTT协议深度教程
java·单片机·struts·apache
极客先躯1 小时前
高级java每日一道面试题-2025年4月01日-微服务篇[Nacos篇]-Nacos集群的数据一致性是如何保证的?
java·开发语言·微服务
麓殇⊙1 小时前
springboot--页面的国际化
java·spring boot·后端
橙序研工坊1 小时前
JavaWeb-01-前端Web开发(HTML+CSS)
java·前端·css·html·javaweb
码农幻想梦1 小时前
4185 费马小定理求逆元
java·开发语言
汤姆大聪明1 小时前
微服务与Spring Cloud Alibaba简介
java·spring boot·spring·spring cloud·微服务
虾球xz2 小时前
游戏引擎学习第197天
java·学习·游戏引擎
唐人街都是苦瓜脸2 小时前
Java中常见的设计模式
java·开发语言·设计模式
你是理想2 小时前
java基础多态------面试八股文
java·开发语言·面试
weisian1512 小时前
Java常用工具算法-6--秘钥托管云服务3--微软zure Key Vault
java·microsoft·安全架构