[设计模式/Java/SpringMVC] 设计模式之适配器模式【18】
概述:适配器模式
模式定义
- 适配器模式是一种结构型设计模式
- 它允许将不兼容的对象转换成可兼容的接口。
- 主要目的: 解决在不改变现有代码的情况下,使不兼容的接口之间能够正常工作,通过创建一个中间转换的适配器来将一个对象转换成我们所需要的接口。
模式的组成

- 目标接口(Target):需要适配的标准接口
定义客户需要的接口
- (被)适配者类(Adaptee):定义一个已经存在的接口,这个接口需要适配。
即:源对象(source):需要被适配的不兼容对象。
- 适配器类(Adapter):实现目标接口,并通过组合或继承的方式调用适配者类中的方法,从而实现目标接口
即:充当中间转换角色,该对象将源对象转换成目标接口。
适用场景
- 当系统的数据和行为都正确,但是【接口】不符合的时候,应该考虑使用适配器模式
- 目的是:使控制范围之外的一个原有对象与某个接口匹配。
- 
适配器模式主要应用于希望复用一些现存的类,但是接口又与复用环境要求不一致的情况。 
- 
适配器模式包含2种类型:类适配器模式和对象适配器 
- 类适配器模式,通过多重继承对一个接口与另一个接口进行匹配。
注意:C#、Java等语言不支持多重继承。
- 对象适配器,适配器实现了其中一个对象的接口, 并对另一个对象进行封装。 所有流行的编程语言都可以实现适配器。
- 类适配器结构图
- 对象适配器结构图
模式特点
优点
- 安全可靠:封装了旧接口,对客户端透明,客户端代码无需修改。
- 提高复用性:可以复用不兼容的类;可以对不同的类无需修改,就可以进行组合。
- 扩展性好:在应用程序开发过程中,可以增加新的适配器和被适配对象。
缺点
- 过多的适配器会导致系统结构复杂。
- 如果适配器没有实现好,可能会拖慢整个系统的性能。
- 滥用适配器模式会导致系统设计紊乱。
案例实践
CASE 生活场景(电压转换器/音视频转换器/USB与TypeC转换器/...)
- 
电压转换器:不同国家的电压规格各异,同样功率的电器在不同的地方工作时需要不同的电压,电压转换器作为适配器,将不同电压转换成电器使用标准电压。 
- 
耳机转接头:有些手机没有耳机插口,需要使用转接头适配器,将耳机转换为手机支持的接口,实现对不同的耳机兼容。 
CASE MediaAdapter 基于 MediaPlayer (多媒体播放器)接口,兼容适配支持其他格式的音频文件
需求描述
- 我们有一个 MediaPlayer(多媒体播放器)接口和一个实现了MediaPlayer接口的实体类AudioPlayer(音频播放器)。
默认情况下,
AudioPlayer可以播放mp3格式的音频文件。
- 我们还有另一个接口 AdvancedMediaPlayer(先进的多媒体播放器) 和实现了AdvancedMediaPlayer接口的实体类。
该类可以播放
vlc和mp4格式的文件。
- 
我们想要让 AudioPlayer播放其他格式的音频文件。
- 
为了实现这个功能,我们需要创建一个实现了 MediaPlayer接口的适配器类MediaAdapter,并使用AdvancedMediaPlayer对象来播放所需的格式。
- 
AudioPlayer使用适配器类MediaAdapter传递所需的音频类型,不需要知道能播放所需格式音频的实际类。
- 
AdapterPatternDemo类使用AudioPlayer类来播放各种格式。

MediaPlayer.java / AdvancedMediaPlayer.java
为媒体播放器和更高级的媒体播放器创建接口。
- MediaPlayer.java
public interface MediaPlayer {
   public void play(String audioType, String fileName);
}
- AdvancedMediaPlayer.java
public interface AdvancedMediaPlayer { 
   public void playVlc(String fileName);
   public void playMp4(String fileName);
}
VlcPlayer.java / Mp4Player.java
创建实现了 AdvancedMediaPlayer 接口的实体类。
- VlcPlayer.java
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) {
      //什么也不做
   }
}
- Mp4Player.java
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);      
   }
}
MediaAdapter.java
创建实现了 MediaPlayer 接口的适配器类。
- MediaAdapter.java
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);
      }
   }
}
AudioPlayer.java
创建实现了 MediaPlayer 接口的实体类。
- AudioPlayer.java
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");
      }
   }   
}
AdapterPatternDemo.java
使用 AudioPlayer 来播放不同类型的音频格式。
- AdapterPatternDemo.java
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");
   }
}
out
Playing mp3 file. Name: beyond the horizon.mp3
Playing mp4 file. Name: alone.mp4
Playing vlc file. Name: far far away.vlc
Invalid media. avi format not supported
CASE JDBC 驱动程序
- JDBC驱动程序
不同的数据库提供商实现了不同的JDBC驱动接口,使用适配器模式可以将这些不同的接口适配为标准的JDBC接口,提高应用程序的可移植性。
CASE 日志框架
- 日志框架
Java中有多个常用的日志框架,如Log4j、SLF4J等,不同的日志框架提供的API不同,使用适配器模式可以将这些不同的API适配为一个统一的接口,方便再程序中进行日志记录和管理。
CASE 第三方库或SDK
- 第三方库或SDK
在使用第三方库或 SDK 时,可能由于它们实现的 API 不同而导致应用程序复杂,使用适配器模式可以将不同的 API 适配为统一的接口,简化应用程序的调用。
CASE Spring MVC 的 HandlerAdapter
参考文献
Spring MVC 的请求流程

- 当用户发起请求时,会将请求直接传入前端控制器 DispatcherServlet。
- 它是
Spring MVC核心组件,负责接收请求并分发到各自的处理器中
DispatcherServlet接收到请求后,会调用HandlerMapping组件。
- HandlerMapping根据请求的 URL 查找合适的处理器(- Handler)。
- 这里的
Handler通常是指Controller中的方法,这些方法通常带有@RequestMapping注解(或其变体如@GetMapping,@PostMapping等)。
- 找到合适的处理器(Handler)后,DispatcherServlet需要找到一个能够处理这个处理器的HandlerAdapter。
为此,它会遍历所有已注册的
HandlerAdapter,调用它们的supports方法,检查它们是否支持当前处理器。
- 
找到支持当前处理器的 HandlerAdapter后,DispatcherServlet会调用该HandlerAdapter的handle方法,将请求(Request)委托给其内的处理器(Handler)进行处理
- 
Handler执行业务逻辑,在处理完请求后,会返回一个ModelAndView对象给HandlerAdapter。
注:
ModelAndView对象中包含模型数据(Model)和视图名称(View)。
- 
DispatcherServlet从HandlerAdapter获取到ModelAndView对象后,会调用ViewResolver组件,同时传递ModelAndView对象给该组件。
- 
ViewResolver负责将视图名称解析为实际的视图对象。
- 
DispatcherServlet获取到具体的视图对象后,将模型数据传递给视图对象View进行渲染。
- 
渲染完成后, DispatcherServlet将生成的最终响应内容返回给用户,浏览器或其他客户端显示最终的响应结果。
Spring MVC 的内置适配器(HandlerAdapter)
- Spring MVC提供了一些内置的- HandlerAdapter实现
如
RequestMappingHandlerAdapter、SimpleControllerHandlerAdapter、HttpRequestHandlerAdapter是 Spring MVC 中内置的几个 HandlerAdapter 实现
它们分别用于支持不同类型的处理器。
- RequestMappingHandlerAdapter:支持基于注解的处理器,如使用- @Controller和- @RequestMapping注解的处理器方法。
这是 Spring MVC 中最常用的处理器类型。
- SimpleControllerHandlerAdapter:支持实现- org.springframework.web.servlet.mvc.Controller接口的处理器。
这是 Spring MVC 早期版本中的处理器类型,现在已经较少使用。
- HttpRequestHandlerAdapter:支持实现- org.springframework.web.HttpRequestHandler接口的处理器。
这种类型的处理器主要用于处理静态资源,如图片、样式表等。
自定义适配器
- 要自定义一个 HandlerAdapter,你需要实现org.springframework.web.servlet.HandlerAdapter接口,并提供对你的自定义处理器的支持。
下面是一个简单的自定义
HandlerAdapter示例:
- 首先,创建一个自定义处理器:
public class CustomHandler {
    public String handleRequest() {
        return "Hello, CustomHandler!";
    }
}
- 接着,实现一个自定义的 HandlerAdapter:
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class CustomHandlerAdapter implements HandlerAdapter {
    @Override
    public boolean supports(Object handler) {
        return handler instanceof CustomHandler;
    }
    @Override
    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        CustomHandler customHandler = (CustomHandler) handler;
        String result = customHandler.handleRequest();
        return new ModelAndView("customView", "message", result);
    }
    @Override
    public long getLastModified(HttpServletRequest request, Object handler) {
        return -1;
    }
}
- 要在 Spring MVC 应用中使用这个自定义的 HandlerAdapter,你需要将其注册到DispatcherServlet中。
在基于 Java 配置的应用中,你可以这样做:
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
    @Override
    protected void addAdapters(List<HandlerAdapter> adapters) {
        adapters.add(new CustomHandlerAdapter());
        super.addAdapters(adapters);
    }
}
这样,你的自定义
HandlerAdapter就会在 Spring MVC 应用中生效,并能处理CustomHandler类型的处理器。
Y 推荐文献
X 参考文献
 
    本文链接: https://www.cnblogs.com/johnnyzen
关于博文:评论和私信会在第一时间回复,或直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
日常交流:大数据与软件开发-QQ交流群: 774386015 【入群二维码】参见左下角。您的支持、鼓励是博主技术写作的重要动力!



 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号