【适配器模式】

142 阅读5分钟

Adapter Pattern

  • 适配器模式是一种结构型设计模式
  • 它允许将一个类的接口转换成客户期望的另一个接口
  • 适配器模式使得原本由于接口不兼容而不能一起工作的类可以一起工作。

适配器模式主要有以下几个角色:

一、目标接口(Target)

定义了客户所期望的接口。客户使用这个接口与需要适配的对象进行交互。

二、源对象(Adaptee)

需要被适配的对象,它拥有一个已存在的接口,但这个接口与客户所期望的接口不兼容。

三、适配器(Adapter)

将源对象的接口转换成目标接口,使得客户可以通过目标接口来调用源对象的功能。

适配器模式可以分为类适配器模式和对象适配器模式:

1. 类适配器模式

通过继承源对象类和实现目标接口来实现适配器。这种方式在 Java 中要求源对象必须是一个具体类,而不是抽象类或接口,因为 Java 不支持多继承。

2. 对象适配器模式

通过组合源对象来实现适配器。这种方式更加灵活,因为它不依赖于继承关系,可以适配任何类型的源对象。

业务

类图

classDiagram
    class DomesticSocket {
        <<interface>>
        +supplyPower()
    }
    class ForeignPlug {
        +foreignPowerSupply()
    }
    class PowerAdapter {
        -ForeignPlug foreignPlug
        +PowerAdapter(ForeignPlug)
        +supplyPower()
    }
    DomesticSocket <|.. PowerAdapter
    PowerAdapter --> ForeignPlug

代码

一、电源适配器场景

假设你有一个国外带回来的电器,它的插头是国外标准的,不能直接插入国内的插座。这时候就需要一个电源适配器,将国内插座的接口转换为电器所需的国外插头接口。

  1. 首先定义目标接口(国内插座标准):
interface DomesticSocket {
    void supplyPower();
}
  1. 源对象(国外电器插头):
class ForeignPlug {
    public void foreignPowerSupply() {
        System.out.println("Foreign plug supplying power in foreign standard.");
    }
}
  1. 适配器(电源适配器):
class PowerAdapter implements DomesticSocket {
    private ForeignPlug foreignPlug;

    public PowerAdapter(ForeignPlug foreignPlug) {
        this.foreignPlug = foreignPlug;
    }

    @Override
    public void supplyPower() {
        foreignPlug.foreignPowerSupply();
        System.out.println("Adapter converts power supply to domestic standard.");
    }
}
  1. 客户端使用:
public class AdapterExample {
    public static void main(String[] args) {
        ForeignPlug foreignPlug = new ForeignPlug();
        DomesticSocket adapter = new PowerAdapter(foreignPlug);
        adapter.supplyPower();
    }
}

在这个场景中,电源适配器将国外电器插头的接口转换为国内插座标准的接口,使得国外电器可以在国内正常使用电源。

框架中的应用

java

在 Java 框架中,适配器模式有很多实际的应用场景。

一、java.util.Arrays.asList()方法 java.util.Arrays.asList()方法可以将一个数组转换为一个固定大小的List视图。这实际上是一种适配器模式的应用,它将数组的接口适配为List接口,使得可以使用List接口的方法来操作数组。

例如:

import java.util.Arrays;
import java.util.List;

public class ArraysAsListExample {
    public static void main(String[] args) {
        Integer[] array = {1, 2, 3};
        List<Integer> list = Arrays.asList(array);
        System.out.println(list);
    }
}

二、java.io.InputStreamReaderjava.io.OutputStreamWriter

这两个类是字节流和字符流之间的适配器。InputStreamReader将字节输入流转换为字符输入流,OutputStreamWriter将字符输出流转换为字节输出流。

例如:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class StreamAdapterExample {
    public static void main(String[] args) {
        try {
            FileInputStream fis = new FileInputStream("input.txt");
            InputStreamReader isr = new InputStreamReader(fis);

            FileOutputStream fos = new FileOutputStream("output.txt");
            OutputStreamWriter osw = new OutputStreamWriter(fos);

            int c;
            while ((c = isr.read())!= -1) {
                osw.write(c);
            }

            isr.close();
            osw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

spring

一、Spring MVC 中的HandlerAdapter

在 Spring MVC 中,HandlerAdapter用于处理不同类型的控制器方法。它将控制器方法的调用适配到DispatcherServlet的请求处理流程中。不同的HandlerAdapter实现可以处理不同类型的控制器方法签名,例如带有@RequestMapping注解的方法、基于注解的 RESTful 服务方法等。

例如:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;

@Configuration
@EnableWebMvc
public class SpringMVCAdapterExample implements WebMvcConfigurer {
    @Autowired
    private RequestMappingHandlerAdapter requestMappingHandlerAdapter;

    @Bean
    public MyController myController() {
        return new MyController();
    }

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        configurer.setUseSuffixPatternMatch(false);
    }

    @RestController
    public static class MyController {
        @GetMapping(value = "/hello", produces = MediaType.TEXT_PLAIN_VALUE)
        public String hello() {
            return "Hello, World!";
        }
    }
}

在这个例子中,RequestMappingHandlerAdapter是一个HandlerAdapter的实现,它用于处理带有@RequestMapping注解的控制器方法。

mybatis

在 MyBatis 框架中,适配器模式也有一定的应用。

一、ResultSetHandler接口的实现类

MyBatis 在将数据库查询结果映射到 Java 对象时,使用了适配器模式。ResultSetHandler接口定义了处理结果集的方法,而不同的实现类(如DefaultResultSetHandler)将 JDBC 的ResultSet接口适配为 MyBatis 特定的结果映射逻辑。

例如,当执行一个 SQL 查询后,MyBatis 通过ResultSetHandler的实现类将ResultSet中的数据转换为 Java 对象。这个过程中,ResultSetHandler的实现类就像一个适配器,将 JDBC 的结果集接口转换为 MyBatis 所期望的对象映射接口。

二、插件机制

MyBatis 的插件机制也可以看作是适配器模式的一种应用。插件可以拦截 MyBatis 的四大核心对象(ExecutorStatementHandlerParameterHandlerResultSetHandler)的方法调用,并在执行前后进行一些额外的处理。插件实际上是将原始的核心对象的接口适配为新的接口,增加了一些特定的功能。

例如,一个分页插件可以拦截Executor的查询方法,在查询前后进行分页处理。插件通过实现Interceptor接口,然后将插件包装在目标对象上,实现了对目标对象接口的扩展和适配。

通过这些方式,适配器模式使得 MyBatis 能够更加灵活地处理不同的数据源和功能需求,提高了框架的可扩展性和可维护性。

总结

适配器模式使得原本由于接口不兼容而不能一起工作的类可以一起工作