适配器模式——以springboot为例

什么是适配器模式?

适配器模式(Adapter Pattern)是一种结构型设计模式,它允许接口不兼容的对象能够相互合作。简单来说,适配器模式就像我们日常生活中的电源适配器,它能够连接两个原本不兼容的接口,使它们能够一起工作。

适配器模式的核心思想是:将一个类的接口转换成客户端所期望的另一个接口,使得原本因接口不兼容而无法一起工作的类能够协同工作。

适配器模式的结构

适配器模式主要包含以下角色:

  1. 目标接口(Target):目标接口,客户端所期望的接口
  2. 适配者(Adaptee):需要被适配的类或接口,但是与Target不兼容
  3. 适配器(Adapter):连接目标接口和适配者的中间件
  4. 用户(Client):需要适配器的对象

适配器模式有两种实现方式:

  • 类适配器:使用继承的方式
  • 对象适配器:使用组合的方式(更常用)

一个简单的适配器模式示例

假设我们有一个老系统中的 LegacyUser 类,但我们的新系统需要使用 User 接口。我们可以创建一个适配器来解决这个问题:

java 复制代码
// 目标接口 (Target)
public interface User {
    String getName();
    String getEmail();
    String getPhone();
}

// 适配者 (Adaptee) - 老系统中的类
public class LegacyUser {
    private String username;
    private String contact;
    private String telephone;
    
    // 构造函数、getter和setter省略
    
    public String getUsername() {
        return username;
    }
    
    public String getContact() {
        return contact;
    }
    
    public String getTelephone() {
        return telephone;
    }
}

// 适配器 (Adapter)
public class LegacyUserAdapter implements User {
    private LegacyUser legacyUser;
    
    public LegacyUserAdapter(LegacyUser legacyUser) {
        this.legacyUser = legacyUser;
    }
    
    @Override
    public String getName() {
        return legacyUser.getUsername();
    }
    
    @Override
    public String getEmail() {
        return legacyUser.getContact();
    }
    
    @Override
    public String getPhone() {
        return legacyUser.getTelephone();
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        // 创建一个老系统的用户
        LegacyUser oldUser = new LegacyUser();
        oldUser.setUsername("张三");
        oldUser.setContact("zhangsan@example.com");
        oldUser.setTelephone("13800138000");
        
        // 使用适配器将老系统用户适配到新系统
        User adaptedUser = new LegacyUserAdapter(oldUser);
        
        // 现在可以使用新系统的接口了
        System.out.println("姓名: " + adaptedUser.getName());
        System.out.println("邮箱: " + adaptedUser.getEmail());
        System.out.println("电话: " + adaptedUser.getPhone());
    }
}

Spring Boot 中的适配器模式

Spring Boot 框架中大量使用了适配器模式,下面我们来看几个典型的例子:

1. Spring MVC 中的 HandlerAdapter

在 Spring MVC 中,DispatcherServlet 是前端控制器,负责将请求分发给不同的处理器(Handler)。但是这些处理器的类型可能各不相同(如 Controller、HttpRequestHandler 等),它们的接口也不一致。

为了统一处理这些不同类型的处理器,Spring MVC 使用了适配器模式,引入了 HandlerAdapter 接口:

java 复制代码
// 目标接口
public interface HandlerAdapter {
    boolean supports(Object handler);
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
    long getLastModified(HttpServletRequest request, Object handler);
}

Spring MVC 为不同类型的处理器提供了不同的适配器实现:

java 复制代码
// 适配器实现示例 - 为 @RequestMapping 注解的控制器提供适配
public class RequestMappingHandlerAdapter implements HandlerAdapter {
    @Override
    public boolean supports(Object handler) {
        return handler instanceof HandlerMethod;
    }
    
    @Override
    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 处理请求的逻辑
        return handleInternal(request, response, (HandlerMethod) handler);
    }
    
    // 其他方法实现...
}

DispatcherServlet 的处理流程大致如下:

java 复制代码
// DispatcherServlet 中的处理逻辑(简化版)
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    // 找到处理请求的 handler
    Object handler = getHandler(request);
    
    // 找到适合该 handler 的 HandlerAdapter
    HandlerAdapter ha = getHandlerAdapter(handler);
    
    // 使用适配器处理请求
    ModelAndView mv = ha.handle(request, response, handler);
    
    // 处理视图...
}

// 查找合适的适配器
private HandlerAdapter getHandlerAdapter(Object handler) {
    for (HandlerAdapter ha : this.handlerAdapters) {
        if (ha.supports(handler)) {
            return ha;
        }
    }
    throw new ServletException("No adapter for handler [" + handler + "]");
}

通过这种方式,Spring MVC 可以支持多种类型的处理器,而 DispatcherServlet 不需要知道具体处理器的实现细节。

2. Spring Boot 中的 WebMvcConfigurer 适配器

在 Spring Boot 2.0 之前,我们通常通过继承 WebMvcConfigurerAdapter 来自定义 MVC 配置:

java 复制代码
// Spring Boot 1.x 中的用法
public class WebConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyInterceptor());
    }
}

WebMvcConfigurerAdapter 就是一个适配器类,它实现了 WebMvcConfigurer 接口的所有方法(提供空实现),这样我们只需要重写需要的方法即可。

在 Java 8 之后,接口可以有默认方法,所以在 Spring Boot 2.0 中,WebMvcConfigurerAdapter 被废弃了,我们可以直接实现 WebMvcConfigurer 接口:

java 复制代码
// Spring Boot 2.x 中的用法
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyInterceptor());
    }
}

3. Spring Boot 中的 MessageConverter

Spring Boot 中的 HttpMessageConverter 也是适配器模式的一个很好的例子。它负责将 HTTP 请求体转换为 Java 对象,或将 Java 对象转换为 HTTP 响应体。

java 复制代码
// 目标接口
public interface HttpMessageConverter<T> {

    boolean canRead(Class<?> clazz, MediaType mediaType);
    
    boolean canWrite(Class<?> clazz, MediaType mediaType);
    
    List<MediaType> getSupportedMediaTypes();
    
    T read(Class<? extends T> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException;
    
    void write(T t, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException;
}

Spring Boot 提供了多种 HttpMessageConverter 的实现,如 MappingJackson2HttpMessageConverter(处理 JSON)、MarshallingHttpMessageConverter(处理 XML)等。

实现一个自定义的 Spring Boot 适配器

让我们实现一个自定义的适配器,将第三方 API 的数据格式适配到我们的系统中:

假设我们有一个第三方天气 API,但它的数据格式与我们系统需要的格式不同:

java 复制代码
// 第三方天气 API 提供的数据格式
public class ThirdPartyWeatherData {
    private String cityCode;
    private double temp;
    private int humidity;
    private String weatherCondition;
    
    // 构造函数、getter和setter省略
}

// 我们系统中使用的天气数据格式
public interface WeatherInfo {
    String getCityName();
    double getTemperature();
    int getHumidity();
    String getCondition();
    boolean isRainy();
}

// 适配器实现
@Service
public class WeatherAdapter implements WeatherInfo {
    private final ThirdPartyWeatherData thirdPartyData;
    private final CityService cityService; // 用于将城市代码转换为城市名称
    
    @Autowired
    public WeatherAdapter(ThirdPartyWeatherService thirdPartyService, CityService cityService) {
        this.thirdPartyData = thirdPartyService.getWeatherData();
        this.cityService = cityService;
    }
    
    @Override
    public String getCityName() {
        // 将城市代码转换为城市名称
        return cityService.getCityNameByCode(thirdPartyData.getCityCode());
    }
    
    @Override
    public double getTemperature() {
        // 假设第三方API返回的是华氏度,我们需要转换为摄氏度
        return (thirdPartyData.getTemp() - 32) * 5 / 9;
    }
    
    @Override
    public int getHumidity() {
        return thirdPartyData.getHumidity();
    }
    
    @Override
    public String getCondition() {
        return thirdPartyData.getWeatherCondition();
    }
    
    @Override
    public boolean isRainy() {
        // 根据天气状况判断是否下雨
        String condition = thirdPartyData.getWeatherCondition().toLowerCase();
        return condition.contains("rain") || condition.contains("drizzle");
    }
}

// 控制器中使用适配器
@RestController
@RequestMapping("/weather")
public class WeatherController {
    private final WeatherInfo weatherInfo;
    
    @Autowired
    public WeatherController(WeatherAdapter weatherAdapter) {
        this.weatherInfo = weatherAdapter;
    }
    
    @GetMapping("/current")
    public Map<String, Object> getCurrentWeather() {
        Map<String, Object> result = new HashMap<>();
        result.put("city", weatherInfo.getCityName());
        result.put("temperature", weatherInfo.getTemperature());
        result.put("humidity", weatherInfo.getHumidity());
        result.put("condition", weatherInfo.getCondition());
        result.put("isRainy", weatherInfo.isRainy());
        return result;
    }
}

适配器模式的优缺点

优点:

  1. 增加了类的透明性:通过适配器,客户端可以调用同一接口,无需关心底层实现
  2. 提高了类的复用性:适配器可以让原本不兼容的类一起工作
  3. 灵活性和扩展性好:可以在不修改原有代码的情况下,引入并使用新的组件

缺点:

  1. 增加了系统的复杂性:引入了新的类和接口
  2. 可能会导致代码可读性下降:如果过度使用适配器,可能会让系统变得难以理解

适配器模式的应用场景

  1. 需要使用一个已存在的类,但其接口不符合需求时
  2. 需要统一多个类的接口时
  3. 需要复用一些现有的类,但不能修改其源代码时
  4. 需要在新旧系统之间建立联系时

总结

适配器模式是一种非常实用的设计模式,它帮助我们解决接口不兼容的问题。在 Spring Boot 等框架中,适配器模式被广泛应用,使得框架能够灵活地支持各种不同的组件和实现。

相关推荐
带刺的坐椅4 分钟前
Solon v3.4.2(Java 应用开发生态基座)
java·ai·solon·liteflow·mcp
lang2015092836 分钟前
Apache Ignite 与 Spring Boot 集成
spring boot·后端·apache·ignite
小醉你真好1 小时前
Spring Boot 数据源配置中为什么可以不用写 driver-class-name
spring boot·后端·源代码管理
SirLancelot11 小时前
数据结构-Set集合(一)Set集合介绍、优缺点
java·开发语言·数据结构·后端·算法·哈希算法·set
haaaaaaarry1 小时前
Element Plus常见基础组件(一)
java·前端·javascript·vue.js
歌者長門1 小时前
做题笔记:某大讯飞真题28道
java·数据结构·算法
Savvy..1 小时前
Day05 Maven
java·junit·maven·注解
Goboy2 小时前
我是如何设计出高性能群消息已读回执系统的
java·后端·架构
阳光明媚sunny2 小时前
结构型设计模式
java·设计模式
码luffyliu2 小时前
Java:高频面试知识分享1
java·八股文