设计模式基础概念(结构型模式):适配器模式(Adapter Pattern)

概述

适配器模式是一种结构型设计模式, 它能使接口不兼容的对象能够相互合作

适配器模式通过封装对象将复杂的转换过程隐藏于幕后。 被封装的对象甚至察觉不到适配器的存在

例如, 你可以使用一个将所有数据转换为英制单位 (如英尺和英里) 的适配器封装运行于米和千米单位制中的对象。

适配器不仅可以转换不同格式的数据, 其还有助于采用不同接口的对象之间的合作

它的运作方式如下:

  • 适配器实现与其中一个现有对象兼容的接口。
  • 现有对象可以使用该接口安全地调用适配器方法。
  • 适配器方法被调用后将以另一个对象兼容的格式和顺序将请求传递给该对象
  • 有时你甚至可以创建一个双向适配器来实现双向转换调用

结构

对象适配器

实现时使用了构成原则适配器实现了其中一个对象的接口, 并对另一个对象进行封装

所有流行的编程语言都可以实现适配器。

类适配器

这一实现使用了继承机制: 适配器同时继承两个对象的接口。

请注意, 这种方式仅能在支持多重继承的编程语言中实现, 例如 C++

java现有示例及识别方法

使用示例: 适配器模式在 Java 代码中很常见。 基于一些遗留代码的系统常常会使用该模式 。 在这种情况下, 适配器让遗留代码与现代的类得以相互合作。

Java 核心程序库中有一些标准的适配器:

  • java.util.Arrays#asList()
  • java.util.Collections#list()
  • java.util.Collections#enumeration()
  • java.io.InputStreamReader(InputStream) (返回 Reader对象)
  • java.io.OutputStreamWriter(OutputStream) (返回 Writer对象)
  • javax.xml.bind.annotation.adapters.XmlAdapter#marshal() 和 #unmarshal()

识别方法 : 适配器可以通过以不同抽象或接口类型实例为参数的构造函数来识别 。 当适配器的任何方法被调用时, 它会将参数转换为合适的格式, 然后将调用定向到其封装对象中的一个或多个方法。

伪代码实现

客户端接口 (Client Interface)

客户端接口 (Client Interface) 描述了其他类与客户端代码合作时必须遵循的协议

定义用户实际需要的接口

java 复制代码
abstract class Target {
    public abstract void Request();
}

服务 (Service)

服务 (Service) 中有一些功能类 (通常来自第三方或遗留系统)。 客户端与其接口不兼容, 因此无法直接调用其功能

定义一个需要适配的接口

java 复制代码
class Adaptee {
    public void SpecificRequest() {
        System.out.println("特殊请求");
    }
}

适配器 (Adapter)

适配器 (Adapter) 是一个可以同时与客户端和服务交互的类 :它在实现客户端接口的同时封装了服务对象

适配器接受客户端通过适配器接口发起的调用, 并将其转换为适用于被封装服务对象的调用

通过在内部包装一个 Adaptee 对象,把源接口转换成目标接口。

java 复制代码
class Adapter extends Target {
	// 在适配器类中添加一个成员变量用于保存对于服务对象的引用
    private Adaptee adaptee = new Adaptee();
	// 依次实现适配器类客户端接口的所有方法
    @Override
    public void Request() {
        adaptee.SpecificRequest();
    }
}

客户端 (Client)

java 复制代码
public class AdapterPattern {
    public static void main(String[] args) {
        Target target = new Adapter();
        // 客户端必须通过客户端接口使用适配
        target.Request();
    }
}

示例2

将两种完全不同的事物联系到⼀起,就像现实⽣活中的变压器。

假设⼀个⼿机充电器需要的电压是20V,但是正常的电压是 220V,这时候就需要⼀个变压器,

将220V的电压转换成20V的电压,这样,变压器就将20V的电压和⼿机联系起来了。

适配器

java 复制代码
class VoltageAdapter {
    public void changeVoltage() {
        System.out.println("正在充电...");
        System.out.println("原始电压:" + Phone.V + "V");
        System.out.println("经过变压器转换之后的电压:" + (Phone.V - 200) + "V");
    }
}

适配器接受客户端通过适配器接口发起的调用, 并将其转换为适用于被封装服务对象的调用

java 复制代码
class Phone {
    public static final int V = 220;
    private VoltageAdapter adapter;

    public void charge() {
        adapter.changeVoltage();
    }

    public void setAdapter(VoltageAdapter adapter) {
        this.adapter = adapter;
    }
}
java 复制代码
public class Test {
    public static void main(String[] args) {
        Phone phone = new Phone();
        VoltageAdapter adapter = new VoltageAdapter();
        phone.setAdapter(adapter);
        phone.charge();
    }
}

实现方式

  1. 确保至少有两个类的接口不兼容

    一个无法修改 (通常是第三方、 遗留系统或者存在众多已有依赖的类) 的功能性服务类
    一个或多个将受益于使用服务类的客户端类

  2. 声明客户端接口, 描述客户端如何与服务交互。

  3. 创建遵循客户端接口的适配器类。 所有方法暂时都为空。

  4. 在适配器类中添加一个成员变量用于保存对于服务对象的引用

    1. 通常情况下会通过构造函数对该成员变量进行初始化, 但有时在调用其方法时将该变量传递给适配器会更方便。
    2. 依次实现适配器类客户端接口的所有方法。 适配器会将实际工作委派给服务对象, 自身只负责接口或数据格式的转换。
  5. 客户端必须通过客户端接口使用适配器。 这样一来, 你就可以在不影响客户端代码的情况下修改或扩展适配器。

相关推荐
hhzz4 小时前
Python大数据实战(十六):音乐推荐系统——基于协同过滤算法构建个性化歌单引擎
大数据·人工智能·python·数据挖掘·数据分析
SunnyDays10114 小时前
Python PDF 转 Markdown 详解(转换整个文档,特定页面,表格,扫描 PDF)
python·pdf 转 markdown·pdf 转 md·扫描 pdf 转 md·pdf 表格转 md
2601_954706494 小时前
云手机 API 自动化实战:Python 批量控机、挂机脚本完整实现
python·智能手机·自动化
Alan_6918 小时前
聊聊 Swagger/Postman/Apifox 的真实选型与 Python 项目实战
python·测试工具·postman
智慧物业老杨9 小时前
物业绿化数智化权责管控方案:基于工单平台的双层权限追溯模块落地实践
python
Metaphor69210 小时前
使用 Python 在 Word 文档中添加批注
python·word
FreakStudio10 小时前
WIZnet 开源方案分享:W55MH32 开发板 + 小智 AI实现以太网版本的小智机器人
python·单片机·嵌入式·大学生·面向对象·并行计算·电子diy·电子计算机
会周易的程序员10 小时前
使用 pybind11 封装 C++ 日志库 microLog 为 Python 模块
java·c++·python·物联网·架构·日志·aiot
一只专注api接口开发的技术猿10 小时前
电商评论自动化监控与情感数据分析完整落地教程(附可直接运行 Python 代码)
大数据·数据库·python·数据分析·自动化
HAPPY酷10 小时前
【ROS2】 Conda 环境搭建(命令行版)
linux·运维·python·ubuntu·机器人·conda