Skip to content

1. 什么是适配器模式?

适配器模式 (Apdater Pattern):

将一个系统的接口转换成另外一种形式,从而使原来不能直接调用的接口变得可以调用。

2. 为什么使用适配器模式?

  • 在现实生活中,经常出现两个对象因接口不兼容而不能在一起工作的实例,这时需要第三者进行适配。

例如,讲中文的人同讲英文的人对话时需要一个翻译,用直流电的笔记本电脑接交流电源时需要一个电源适配器,用计算机访问照相机的 SD 内存卡时需要一个读卡器等。

  • 在软件设计中也可能出现:需要开发的具有某种业务功能的组件在现有的组件库中已经存在,但它们与当前系统的接口规范不兼容,如果重新开发这些组件成本又很高,这时用适配器模式能很好地解决这些问题。

3. 应用场景

  • 以前开发的系统存在满足新系统功能需求的类,但其接口同新系统的接口不一致。
  • 使用第三方提供的组件,但组件接口定义和自己要求的接口定义不同。
  • 新老版本接口的兼容
  • Mybatis 多种日志框架的整合

4. 原理

如下图结构所示:

适配器模式(Adapter)包含以下主要角色。

  • 目标(Target)接口:

    当前系统业务所期待的接口,它可以是抽象类或接口。

  • 适配者(Adaptee)类:

    它是被访问和适配的现存组件库中的组件接口。

  • 适配器(Adapter)类:

    它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。

5. 适配器模式-案例 1:实现日志收集

需求:设计一个日志收集系统,可能会考虑文件写入、也可能考虑写入 MQ、也可能考虑写入数据库等。

1. 目标(Target)接口

java
package com.log.service;


/**
 * description: 日志写入数据库-目标接口: 当前系统业务所期待的接口,它可以是抽象类或接口。
 * date: 2020/9/21 13:35
 * author: Calvin
 * version: 1.0
 */
public interface LogWriteDbService {

    /**
     * 将本地文件写入到数据库中 新的目标
     */
    void logWriteDb();
}

2. 适配者(Adaptee)类

java
package com.log.service;

import com.log.entity.Log;

import java.util.List;


/**
 * description: 日志写入本地文件-适配者(Adaptee)类:它是被访问和适配的现存组件库中的组件接口。
 * date: 2020/9/21 13:35
 * author: Calvin
 * version: 1.0
 */
public interface LogWriteFileService {

    /**
     * 将日志写入本地文件
     */
    void logWriteFile();

    /**
     * 从本地文件中读取日志
     *
     * @return
     */
    List<Log> readLogFile();

}
java
package com.log.service.impl;

import com.log.entity.Log;
import com.log.service.LogWriteFileService;

import java.util.ArrayList;
import java.util.List;

/**
 * description: 日志写入数据库-目标接口: 当前系统业务所期待的接口,它可以是抽象类或接口。
 * date: 2020/9/21 13:35
 * author: Calvin
 * version: 1.0
 */
public class LogWriteFileServiceImpl implements LogWriteFileService {

    @Override
    public void logWriteFile() {
        System.out.println(">>>将日志写入文件中...");
    }

    @Override
    public List<Log> readLogFile() {
        Log log1 = new Log();
        log1.setLogId("0001");
        log1.setLogContent("Tomcat启动成功..");

        Log log2 = new Log();
        log2.setLogId("0002");
        log2.setLogContent("Jetty启动成功..");

        List<Log> logs = new ArrayList<Log>();
        logs.add(log1);
        logs.add(log2);
        return logs;
    }
}

3. 适配器(Adapter)类

java
package com.log.apdater;

import com.log.entity.Log;
import com.log.service.LogWriteDbService;
import com.log.service.impl.LogWriteFileServiceImpl;

import java.util.List;

/**
 * description: 类适配器模式-适配器(Adapter)类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。
 * date: 2020/9/21 13:35
 * author: Calvin
 * version: 1.0
 */
public class LogAdapter extends LogWriteFileServiceImpl implements LogWriteDbService {

    @Override
    public void logWriteDb() {
        // 1.从文件中读取日志文件
        List<Log> logs = super.readLogFile();
        // 2.写入到数据库中
        System.out.println(">>>将数据写入到数据库中.." + logs);
        // 3.写入到本地文件中
        super.logWriteFile();
    }
}

4. 编写测试

java
package com.log.contoller;

import com.log.apdater.LogAdapter;
import com.log.service.LogWriteFileService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * description: LogController
 * date: 2020/9/21 15:51
 * author: Calvin
 * version: 1.0
 */
@RestController
@RequestMapping(value = "log")
public class LogController {

    @Autowired
    private LogWriteFileService logWriteFileService;

    @GetMapping("old")
    public void write() {
        logWriteFileService.logWriteFile();
    }

    @GetMapping("new")
    public void write1() {
        LogAdapter logAdapter = new LogAdapter();
        logAdapter.logWriteDb();
    }
}

6. 结果演示

代码链接:https://github.com/1016280226/design-patterns

7. 优缺点

优点:

  • 更好的复用性

  • 更好的扩展性

  • 系统需要使用现有的类,而此类的接口不符合系统的需要。那么通过适配器模式就可以让这些功能得到更好的复用。

  • 在实现适配器功能的时候,可以调用自己开发的功能,从而自然地扩展系统的功能。

缺点

  • 过多的使用适配器,会让系统非常零乱,不易整体进行把握。

比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。