type
status
date
slug
summary
tags
category
icon
password
 

一、适配器模式概述

 
适配器模式(Adapter Pattern):将某个类的接口转换成客户端期望的另一个接口表示,主的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。
其别名为包装器(Wrapper) 。适配器模式属于结构型模式。有以下两类:
  • 类适配器模式
  • 对象适配器模式
 
适配器模式结构:
  • 目标(Target)接口:当前系统业务所期待的接口,它可以是抽象类或接口。
  • 适配者(Adaptee)类:它是被访问和适配的现存组件库中的组件接口。
  • 适配器(Adapter)类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。
 
以下将以充电器的例子来讲解适配器,充电器本身相当于 Adapter,220V 交流电相当于 src (即被适配者),我们 的目 dst(即目标)是 5V 直流电。

二、类适配器模式

 
基本介绍:Adapter 类,通过继承 src 类,实现 dst 类接口,完成 src->dst 的适配。
 
 

三、对象适配器模式

 
基本介绍:Adapter 类,持有 src 类实例,实现 dst 类接口,完成 src->dst 的适配。
 
将继承 src 类改为持有 src 类实例,即从继承转变为组合或聚合关系,提升可拓展性。
 
 

四、二者如何选择

 
判断的标准主要有两个,一个是 Adaptee(src) 接口的个数,另一个是 Adaptee 和 Target(dst) 的契合程度。
  • 如果 Adaptee 接口并不多,那两种实现方式都可以。
  • 如果 Adaptee 接口很多,而且 Adaptee 和 ITarget 接口定义大部分都相同,那我们推荐使用类适配器,因为 Adaptor 复用父类 Adaptee 的接口,比起对象适配器的实现方式,Adaptor 的代码量要少一些。
  • 如果 Adaptee 接口很多,而且 Adaptee 和 ITarget 接口定义大部分都不相同,那我们推荐使用对象适配器,因为组合结构相对于继承更加灵活。
 

五、适配器模式应用场景

 
一般来说,适配器模式可以看作一种“补偿模式”,用来补救设计上的缺陷。应用这种模式算是“无奈之举”。如果在设计初期,我们就能协调规避接口不兼容的问题,那这种模式就没有应用的机会了。

封装有缺陷的接口设计

 
假设我们依赖的外部系统在接口设计方面有缺陷(比如包含大量静态方法),引入之后会影响到我们自身代码的可测试性。为了隔离设计上的缺陷,我们希望对外部系统提供的接口进行二次封装,抽象出更好的接口设计,这个时候就可以使用适配器模式了。

统一多个类的接口设计

 
某个功能的实现依赖多个外部系统(或者说类)。通过适配器模式,将它们的接口适配为统一的接口定义,然后我们就可以使用多态的特性来复用代码逻辑。
 
假设我们的系统要对用户输入的文本内容做敏感词过滤,为了提高过滤的召回率,我们引入了多款第三方敏感词过滤系统,依次对用户输入的内容进行过滤,过滤掉尽可能多的敏感词。但是,每个系统提供的过滤接口都是不同的。这就意味着我们没法复用一套逻辑来调用各个系统。这个时候,我们就可以使用适配器模式,将所有系统的接口适配为统一的接口定义,这样我们可以复用调用敏感词过滤的代码。
 

替换依赖的外部系统

 
当我们把项目中依赖的一个外部系统替换为另一个外部系统的时候,利用适配器模式,可以减少对代码的改动。
 
 

兼容老版本接口

 
在做版本升级的时候,对于一些要废弃的接口,我们不直接将其删除,而是暂时保留,并且标注为 deprecated,并将内部实现逻辑委托为新的接口实现。这样做的好处是,让使用它的项目有个过渡期,而不是强制进行代码修改。这也可以粗略地看作适配器模式的一个应用场景。
 

适配不同格式的数据

 
适配器模式主要用于接口的适配,实际上,它还可以用在不同格式的数据之间的适配。比如,把从不同征信系统拉取的不同格式的征信数据,统一为相同的格式,以方便存储和使用。再比如,Java 中的 Arrays.asList() 也可以看作一种数据适配器,将数组类型的数据转化为集合容器类型。
 

适配器模式在 Java 日志中的应用

 
Java 中有很多日志框架,在项目开发中,我们常常用它们来打印日志信息。其中,比较常用的有 log4j、logback,以及 JDK 提供的 JUL(java.util.logging) 和 Apache 的 JCL(Jakarta Commons Logging) 等。
它们并没有实现统一的接口。这主要可能是历史的原因,它不像 JDBC 那样,一开始就制定了数据库操作的接口规范。
Slf4j 日志框架,它相当于 JDBC 规范,提供了一套打印日志的统一接口规范。不过,它只定义了接口,并没有提供具体的实现,需要配合其他日志框架(log4j、logback……)来使用。
Slf4j 的出现晚于 JUL、JCL、log4j 等日志框架,所以,这些日志框架也不可能牺牲掉版本兼容性,将接口改造成符合 Slf4j 接口规范。Slf4j 也事先考虑到了这个问题,所以,它不仅仅提供了统一的接口定义,还提供了针对不同日志框架的适配器。对不同日志框架的接口进行二次封装,适配成统一的 Slf4j 接口定义。
我们统一使用 Slf4j 提供的接口来编写打印日志的代码,具体使用哪种日志框架实现(log4j、logback……),是可以动态地指定的(使用 Java 的 SPI 技术),只需要将相应的 SDK 导入到项目中即可。
 

六、代理 & 桥接 & 装饰器 & 适配器 的区别

 
  1. 代理、桥接、装饰器、适配器,这 4 种模式是比较常用的结构型设计模式。它们的代码结构非常相似。笼统来说,它们都可以称为 Wrapper 模式,也就是通过 Wrapper 类二次封装原始类。
  1. 尽管代码结构相似,但这 4 种设计模式的用意完全不同,也就是说要解决的问题、应用场景不同,这也是它们的主要区别:
      • 代理模式:在不改变原始类接口的条件下,为原始类定义一个代理类,主要目的是控制访问(功能增加),而非加强功能,这是它跟装饰器模式最大的不同。
      • 桥接模式:目的是将接口部分和实现部分分离,一个类存在多个独立变化的维度,我们通过组合的方式,让多个维度可以独立进行扩展。
      • 装饰器模式:在不改变原始类接口的情况下,对原始类功能进行增强,并且支持多个装饰器的嵌套使用
      • 适配器模式:是一种事后补救策略。适配器提供跟原始类不同的接口,而代理模式、装饰器模式提供的都是跟原始类相同的接口。
       
行为型模式之观察者设计模式结构型模式之装饰器设计
Loading...
ITNXD
ITNXD
一个普通的干饭人🍚
最新发布
Java 并发编程
2025-7-31
Spring 源码系列第三章 - 后置 Bean 处理器与 Bean 生命周期
2022-12-25
Spring 源码系列第一章 - Spring 核心组件接口
2022-12-11
Spring 源码系列第二章 - 后置工厂处理器与 Bean 生命周期
2022-12-10
行为型模式之迭代器设计模式
2022-12-2
行为型模式之责任链设计模式
2022-12-1