Java 设计模式——适配器模式:从原理到3种实战的完整指南

Java 设计模式------适配器模式:从原理到3种实战的完整指南

适配器模式是一种核心的结构型设计模式,核心价值在于解决接口不兼容问题。它通过引入中间适配层,在不修改现有代码的前提下,让原本无法协作的接口实现兼容协作,因此也被称为 "包装器模式"。就像中国电器在美国使用需要电源转换插头一样,适配器模式为不同接口之间搭建了 "转换桥梁"。

文章目录

一、核心概念与实际场景

1. 模式核心含义

当系统需要调用的接口(如第三方 SDK、老系统接口)与自身期望的接口格式不匹配时,适配器模式通过定义一个适配类,将目标接口转换为系统可识别的格式,从而实现接口间的无缝协作。其核心是 "兼容现有代码,而非改造现有代码"。

2. 典型应用场景

  • SpringMVC 框架:SpringMVC 支持 HTTP、WebSocket 等多种请求类型,通过HandlerAdapter适配器链,将不同类型的处理器(如@Controller方法、HttpRequestHandler)统一适配为 DispatcherServlet 可调用的接口,实现请求的统一分发。
  • 多团队协作开发:智能设备项目中,硬件团队(团队 A)提供设备控制 SDK(如灯的开关接口),软件团队(团队 B)需基于统一接口控制多种硬件,通过适配器转换参数格式,隔离硬件 SDK 与上层业务逻辑。
  • 第三方组件集成:使用不同厂商的支付 SDK 时,各厂商接口格式差异较大,通过适配器将其统一为系统内部的支付接口,降低集成复杂度。
  • 跨平台兼容:不同操作系统(如 Windows、Linux)的文件操作接口不同,通过适配器封装差异,上层业务可调用统一接口实现跨平台兼容。

二、业务需求定义

本文以多团队协作场景为案例展开实战:

  • 团队 A(硬件团队)已提供灯控 SDK,包含LightService类及对应的入参LightBO,支持灯的开关操作;
  • 团队 B(软件团队)需开发上层控制系统,通过统一的设备控制接口(入参ControlDeviceBO)管理多种硬件,需通过适配器调用团队 A 的 SDK,实现灯的开关控制。

三、非设计模式实现方案(反面案例)

1. 实现思路

直接在业务层中进行参数转换和 SDK 调用,未引入适配层,将接口适配逻辑与业务逻辑混在一起。

2. 项目结构

可以看到,上边分了两个包:controldevice包和device包。controldevice包是互联网项目结构,包括controller、service等;device包可以认为是团队A给的SDK包。

3. 具体代码

控制设备入参类
java 复制代码
package com.boke.desginpattern.adapter.cotroldevice.bo;

import lombok.Data;

@Data
public class ControlDeviceBO {
    /**
     * 设备的ID
     */
    private String deviceId;
    /**
     * 开/关。true:开。false:关
     */
    private Boolean openClose;
}
控制设备测试接口
java 复制代码
package com.boke.desginpattern.adapter.cotroldevice.controller;

import com.boke.desginpattern.adapter.cotroldevice.bo.ControlDeviceBO;
import com.boke.desginpattern.adapter.cotroldevice.service.ControlDeviceService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@RequestMapping("controlDevice")
public class ControlDeviceController {
    @Resource
    private ControlDeviceService controlDeviceService;

    /**
     * 开关灯
     **/
    @PostMapping("lightOpenClose")
    public String lightOpenClose(@RequestBody ControlDeviceBO controlDeviceBO) {
        return controlDeviceService.lightOpenClose(controlDeviceBO);
    }
}
控制设备业务类
java 复制代码
package com.boke.desginpattern.adapter.cotroldevice.service;

import com.boke.desginpattern.adapter.cotroldevice.bo.ControlDeviceBO;
import com.boke.desginpattern.adapter.device.bo.LightBO;
import com.boke.desginpattern.adapter.device.service.LightService;
import org.springframework.stereotype.Service;

@Service
public class ControlDeviceService {
    public String lightOpenClose(ControlDeviceBO controlDeviceBO) {
        System.out.println("上层:进入控制灯开关的service");

        // 新建一个实际控制灯的参数
        LightBO lightBO = new LightBO();
        lightBO.setLightId(controlDeviceBO.getDeviceId());
        lightBO.setOpen(controlDeviceBO.getOpenClose());

        // 直接去控制电灯
        LightService lightService = new LightService();
        lightService.control(lightBO);

        // 现在这种写法是最原始的写法,直接在这里进行参数的转换。
        // 如果换了底层的设备,这里就要新加代码,会越来越长。所以需要使用适配器模式

        return "success";
    }
}
控制灯入参类
java 复制代码
package com.boke.desginpattern.adapter.device.bo;

import lombok.Data;

@Data
public class LightBO {
    /**
     * 灯的ID
     */
    private String lightId;

    /**
     * true:开灯。false:关灯
     */
    private Boolean open;
}
控制灯业务类
java 复制代码
package com.boke.desginpattern.adapter.device.service;

import com.boke.desginpattern.adapter.device.bo.LightBO;

public class LightService {
    public void control(LightBO lightBO) {
        System.out.println("硬件层:开始直接控制灯");

        if (lightBO.getOpen()) {
            System.out.printf("%s设备已开灯\n", lightBO.getLightId());
        } else {
            System.out.printf("%s设备已关灯\n", lightBO.getLightId());
        }
    }
}
测试结果

调用/controlDevice/lightOpenClose接口,传入{"deviceId":"111","openClose":false},控制台输出:

text 复制代码
上层:进入控制灯开关的service
硬件层:开始直接控制灯
111设备已关灯

4. 方案优缺点

  • 优点:实现简单,适合快速验证功能,能直观体现适配需求的痛点;
  • 缺点:
    1. 适配逻辑与业务逻辑耦合,违反单一职责原则;
    2. 新增硬件设备时需修改业务代码,违反开闭原则,导致代码冗余臃肿;
    3. 直接依赖 SDK 具体实现,后续替换 SDK 需大面积修改代码。

四、适配器模式实战(三种实现方式)------ 手动选择适配器

1. 整体设计优化

(1)项目结构
(2)核心改进
  • 提取统一的适配器接口,隔离上层业务与硬件 SDK;
  • 三种适配器实现方式分别应对不同场景,适配逻辑独立维护;
  • 业务层通过调用适配器接口实现功能,不直接依赖 SDK

可以看到,上边分了很多个包:

base包:基础的适配器接口。

adapterclass/adapterinterface/adapterobject包:适配器包,用来适配上层应用与设备层。三个包分别对适配器模式的三种写法。

controldevice包:互联网项目结构,包括controller、service等。

device包:可以认为是灯具厂商给的SDK包,直接控制设备。

2. 具体代码

控制设备入参类
java 复制代码
package com.boke.desginpattern.adapterhand.cotroldevice.bo;

import lombok.Data;

@Data
public class ControlDeviceBO {
    /**
     * 设备的ID
     */
    private String deviceId;

    /**
     * 开/关。true:开。false:关
     */
    private Boolean openClose;
}
控制设备测试接口
java 复制代码
package com.boke.desginpattern.adapterhand.cotroldevice.controller;

import com.boke.desginpattern.adapterhand.cotroldevice.bo.ControlDeviceBO;
import com.boke.desginpattern.adapterhand.cotroldevice.service.ControlDeviceService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@RequestMapping("controlDevice")
public class ControlDeviceController {
    @Resource
    private ControlDeviceService controlDeviceService;

    /**
     * 开关灯
     **/
    @PostMapping("lightOpenClose")
    public String lightOpenClose(@RequestBody ControlDeviceBO controlDeviceBO) {
        return controlDeviceService.lightOpenClose(controlDeviceBO);
    }
}
控制设备业务类
java 复制代码
package com.boke.desginpattern.adapterhand.cotroldevice.service;

import com.boke.desginpattern.adapterhand.adapterclass.impl.ClassLightAdapter;
import com.boke.desginpattern.adapterhand.base.DeviceControlAdapter2;
import com.boke.desginpattern.adapterhand.adapterinterface.impl.InterfaceLightAdapter;
import com.boke.desginpattern.adapterhand.adapterobject.impl.ObjectLightAdapter;
import com.boke.desginpattern.adapterhand.base.DeviceControlAdapter;
import com.boke.desginpattern.adapterhand.cotroldevice.bo.ControlDeviceBO;
import com.boke.desginpattern.adapterhand.device.service.LightService;
import org.springframework.stereotype.Service;


@Service
public class ControlDeviceService {
    public String lightOpenClose(ControlDeviceBO controlDeviceBO) {
        System.out.println("上层:进入控制灯开关的service");

        // 类适配器模式
        DeviceControlAdapter classLightAdapter = new ClassLightAdapter();
        classLightAdapter.openClose(controlDeviceBO);

        // 对象适配器模式
        DeviceControlAdapter objectLightAdapter = new ObjectLightAdapter(new LightService());
        objectLightAdapter.openClose(controlDeviceBO);

        // 接口适配器模式
        DeviceControlAdapter2 interfaceLightAdapter = new InterfaceLightAdapter(new LightService());
        interfaceLightAdapter.openClose(controlDeviceBO);

        return "success";
    }
}
控制设备适配器接口
java 复制代码
package com.boke.desginpattern.adapterclass.adapter;

import com.boke.desginpattern.adapterclass.cotroldevice.bo.ControlDeviceBO;

/**
 * 这里抽象出接口的好处是:
 *      1.impl包可以有多个设备的适配器,外边调用时统一调本接口。
 *      2.添加新的适配功能时很方便:本适配器是控制设备用的,比如:开关、设置设备。
 *          如果想加一个控制设备的操作(如:设置设备的睡眠时间等)就在本接口新增一个抽象方法即可,
 *          然后impl包里的具体适配器也去实现设置设备的睡眠时间的抽象方法
 *             如果灯的适配器不需要实现这个抽象方法,可以再定义一个接口,新的接口继承这个接口即可
 *          如果想加一个读取设备状态的适配器,直接在本接口的同级别新加一个接口即可,
 *          然后impl包里的具体适配器也去实现读设备的接口
 */
public interface DeviceControlAdapter {
    /**
     * 开关设备
     */
    void openClose(ControlDeviceBO controlDeviceBO);
}
(1)类适配器模式

使用继承的方式,将需要适配的类转换为目标接口的子类,实现目标接口的所有方法,同时继承适配类的实现,用以完成一些适配逻辑。

java 复制代码
package com.boke.desginpattern.adapterclass.adapter.impl;

import com.boke.desginpattern.adapterclass.adapter.DeviceControlAdapter;
import com.boke.desginpattern.adapterclass.cotroldevice.bo.ControlDeviceBO;
import com.boke.desginpattern.adapterclass.device.bo.LightBO;
import com.boke.desginpattern.adapterclass.device.service.LightService;

public class LightAdapter extends LightService implements DeviceControlAdapter {

    @Override
    public void openClose(ControlDeviceBO controlDeviceBO) {
        // 新建一个实际控制灯的参数
        LightBO lightBO = new LightBO();
        lightBO.setLightId(controlDeviceBO.getDeviceId());
        lightBO.setOpen(controlDeviceBO.getOpenClose());

        // 直接去控制电灯(调用LightService父类的方法)
        this.control(lightBO);
    }

}
(2)对象适配器模式

通过组合的方式,将适配对象与目标接口组合,实现目标接口的所有方法,并在适配类中调用需要适配对象的方法。

java 复制代码
package com.boke.desginpattern.adapterhand.adapterobject.impl;

import com.boke.desginpattern.adapterhand.base.DeviceControlAdapter;
import com.boke.desginpattern.adapterhand.cotroldevice.bo.ControlDeviceBO;
import com.boke.desginpattern.adapterhand.device.bo.LightBO;
import com.boke.desginpattern.adapterhand.device.service.LightService;

public class ObjectLightAdapter implements DeviceControlAdapter {

    private LightService lightService;

    public ObjectLightAdapter(LightService lightService) {
        this.lightService = lightService;
    }

    @Override
    public void openClose(ControlDeviceBO controlDeviceBO) {
        // 新建一个实际控制灯的参数
        LightBO lightBO = new LightBO();
        lightBO.setLightId(controlDeviceBO.getDeviceId());
        lightBO.setOpen(controlDeviceBO.getOpenClose());

        // 直接去控制电灯(调用LightService父类的方法)
        lightService.control(lightBO);
    }

}
(3)接口适配器模式

主要适用于需要被适配的接口中,只有用到个别接口,也就是说不需要实现它的全部接口。通过一个中间抽象类或接口实现。

控制设备适配器接口

DeviceControlAdapter2:用于解释接口适配器。

java 复制代码
package com.boke.desginpattern.adapterhand.base;

import com.boke.desginpattern.adapterhand.cotroldevice.bo.ControlDeviceBO;

public interface DeviceControlAdapter2 {

    /**
     * 开关设备
     */
    void openClose(ControlDeviceBO controlDeviceBO);

    // 无用方法,仅仅用来说明接口适配器
    void a();

}
中间抽象类
java 复制代码
package com.boke.desginpattern.adapterhand.adapterinterface.impl;

import com.boke.desginpattern.adapterhand.base.DeviceControlAdapter2;
import com.boke.desginpattern.adapterhand.cotroldevice.bo.ControlDeviceBO;
import com.boke.desginpattern.adapterhand.device.bo.LightBO;
import com.boke.desginpattern.adapterhand.device.service.LightService;

public abstract class AbstractAdapter implements DeviceControlAdapter2 {

    private LightService lightService;

    public AbstractAdapter(LightService lightService) {
        this.lightService = lightService;
    }

    @Override
    public void openClose(ControlDeviceBO controlDeviceBO) {
        // 新建一个实际控制灯的参数
        LightBO lightBO = new LightBO();
        lightBO.setLightId(controlDeviceBO.getDeviceId());
        lightBO.setOpen(controlDeviceBO.getOpenClose());

        lightService.control(lightBO);
    }

    @Override
    public void a() {

    }

}
灯的适配器
java 复制代码
package com.boke.desginpattern.adapterhand.adapterinterface.impl;

import com.boke.desginpattern.adapterhand.cotroldevice.bo.ControlDeviceBO;
import com.boke.desginpattern.adapterhand.device.service.LightService;

public class InterfaceLightAdapter extends AbstractAdapter {

    public InterfaceLightAdapter(LightService lightService) {
        super(lightService);
    }

    @Override
    public void openClose(ControlDeviceBO controlDeviceBO) {
        super.openClose(controlDeviceBO);
    }

}
测试结果

启动项目调用接口,后台控制台输出结果:

text 复制代码
上层:进入控制灯开关的service
硬件层:开始直接控制灯
111设备已关灯
硬件层:开始直接控制灯
111设备已关灯
硬件层:开始直接控制灯
111设备已关灯

3. 三种实现方式的核心逻辑对比

实现方式 核心原理 关键依赖 灵活性 优缺点
类适配器 继承适配者类(被适配的旧接口)+ 实现目标接口,通过 "继承" 复用适配者的方法 继承关系(单继承限制) 优点:直接复用适配者类的方法,代码简洁;缺点:受 Java 单继承限制,无法同时适配多个适配者类;适配者类的 final 方法无法重写
对象适配器 实现目标接口 + 组合适配者对象(通过构造器注入),通过 "组合" 调用适配者的方法 组合关系(依赖注入) 优点:无单继承限制,可同时适配多个适配者;适配者对象可动态替换,灵活性强;缺点:需手动调用适配者对象的方法,代码量略多
接口适配器 定义包含多个方法的目标接口 → 提供抽象类实现该接口(空实现所有方法)→ 具体适配器继承抽象类,仅重写需要的方法 抽象类(空实现中间层) 优点:解决 "目标接口方法过多,仅需使用部分方法" 的场景,避免代码冗余;缺点:仅适用于 "目标接口方法数量多且部分方法无需实现" 的场景,通用性较弱

五、适配器模式的适用场景

  1. 重用现有的代码 :适配器模式可以允许我们重用已有的类或接口 ,而不需要修改其原有的代码
  2. 集成老系统 :当现有的系统不满足用户需求 时,需要增加系统功能或接口 。但是,老系统的接口可能与现有的技术、平台不兼容,此时可以采用适配器模式,将现有的接口适配为新的接口,从而实现新系统的集成
  3. 集成第三方组件 :在使用第三方组件时,可能由于它们实现的 API 不同而导致应用程序复杂,此时可以使用适配器模式,将第三方组件提供的 API 适配为自己需要的 API,方便在应用程序中进行调用
  4. 实现跨平台兼容 :在不同平台、不同技术栈之间进行开发时,常常需要适配不同的接口,以使得不同的平台或技术栈之间能够相互兼容 ,此时可以使用适配器模式来处理各种不兼容问题
相关推荐
Meteors.7 小时前
23种设计模式——状态模式(State Pattern)
java·设计模式·状态模式
星星点点洲9 小时前
PostgreSQL 15二进制文件
开发语言·设计模式·golang
yaoxin52112310 小时前
211. Java 异常 - Java 异常机制总结
java·开发语言·python
Predestination王瀞潞14 小时前
Java EE开发技术(Servlet整合JDBC银行管理系统-上)
java·servlet·java-ee·jdbc
寻星探路14 小时前
Java EE初阶启程记13---JUC(java.util.concurrent) 的常见类
java·开发语言·java-ee
怪兽201415 小时前
什么是 Redis?
java·数据库·redis·缓存·面试
Gu_yyqx15 小时前
Java 队列
java
落日漫游15 小时前
数据结构笔试核心考点
java·开发语言·算法
疯狂吧小飞牛15 小时前
Lua C API 中的注册表介绍
java·c语言·lua