前言
适配器模式(Adapter Pattern
)的核心将某个类的接口转换成客户端期望的另一个接口表示,使得客户端能够通过自己期望的接口与不兼容的类进行交互。适配器模式有三种实现方式,分别是类适配器模式
、对象适配器模式
、 接口适配器模式
。
我们假设有个视频输出盒子,当前只能接收和输出HDMI
信号,现在客户的输入设备只能输出VGA
信号,则我们可以通过转换器(要适配的类),将VGA
信号转换成HDMI
信号做输出。
实现
1、类适配器模式
客户目标接口,客户的新设备只能输出VGA
信号,无法输出HDMI
信号。
/*** 客户目标输入接口*/
interface SourceInput {fun inputVGA(signal:String)
}
Modem
盒子只接收且输出HDMI
信号,调制后输出给到显示设备进行显示。
/*** 要适配的类*/
open class ModemOutput {fun outputHDMI(signal:String){println("设备输出${signal}给显示设备")}
}
新的输入设备输出VGA
模拟信号不满足现在的需求,我们通过定义转换器OutputAdapter
,将VGA
模拟信号转化成HDMI
数字信号进行输出到Modem
盒子中。
/*** 转换器*/
class OutputAdapter : ModemOutput(), SourceInput {override fun inputVGA(signal: String) {if ("VGA模拟信号" == signal) {println("【转HDMI信号转换器】>> 接收到了源设备,输入的${signal}")val digital = convertVGAToHDMI(signal)outputHDMI(digital)} else {throw IllegalArgumentException("输入信号错误")}}private fun convertVGAToHDMI(signal: String): String {println("【转HDMI信号转换器】>> 转化器将${signal}转化成HDMI数字信号")return "HDMI数字信号"}
}
客户端
val adapter = OutputAdapter() as SourceInput
adapter.inputVGA("VGA模拟信号")// 【转HDMI信号转换器】>> 接收到了源设备,输入的VGA模拟信号
// 【转HDMI信号转换器】>> VGA转HDMI转化器将VGA模拟信号转化成HDMI数字信号
// 设备输出HDMI数字信号给显示设备
适配器类需要继承ModemOutput
,Java
是单继承模式,所以目标文件必须要是接口。
2、对象适配器模式
对象适配器模式较类适配器模式相比,原来的适配器不再继承要适配的类,将其作为适配器的构造参数传入,把继承解耦。
/*** 要适配的类*/
open class ModemOutput {fun outputHDMI(signal:String){println("设备输出${signal}给显示设备")}
}
/*** 目标接口*/
interface SourceInput {fun inputVGA(signal:String)
}
/*** 转换器*/
class OutputAdapter(output: ModemOutput) : SourceInput {private var mOutput:ModemOutput = outputoverride fun inputVGA(signal: String) {if ("VGA模拟信号" == signal) {println("【转HDMI信号转换器】>> 接收到了源设备,输入的${signal}")val digital = convertVGAToHDMI(analog)mOutput.outputHDMI(signal)} else {throw IllegalArgumentException("输入信号错误")}}private fun convertVGAToHDMI(signal: String): String {println("【转HDMI信号转换器】>> VGA转HDMI转化器将${signal}转化成HDMI数字信号")return "HDMI数字信号"}}
3、接口适配器模式
接口适配器模式适用于目标接口中有多个方法,上面我们举了个目标接口中输入VGA
的例子,假如还有DP接口
、DVI接口
的输入,适配器类在实现目标接口时候就要都实现其他的方法,但是客户端输入只有一种,不需要实现全部接口时,如果适配器定义抽象类来实现接口,并且接口中方法空实现,可以灵活解决这个问题。
目标接口新增格式信源输入方法,要适配的类不变。
/*** 目标接口*/
interface SourceInput {fun inputVGA(signal:String)fun inputDP(signal:String)fun inputDVI(signal:String)
}
/*** 要适配的类*/
open class ModemOutput {fun outputHDMI(signal:String){println("设备输出${signal}给显示设备")}
}
新增抽象类,继承源文件ModemOutput
、实现目标接口,覆写空方法,不做具体实现。
abstract class OutputAdapter:ModemOutput(),SourceInput {override fun inputDP(signal: String) {TODO("Not yet implemented")}override fun inputDVI(signal: String) {TODO("Not yet implemented")}override fun inputVGA(signal: String) {TODO("Not yet implemented")}
}
客户端只根据自己需要的,具体实现信号输入方法。
val adapter = object : OutputAdapter() {override fun inputDVI(signal: String) {super.inputDVI(signal)println("【转HDMI信号转换器】>> 接收到了源设备,输入的${signal}")val digital = convertSignalToHDMI(signal)outputHDMI(digital)}}private fun convertSignalToHDMI(signal: String): String {when (signal) {"VGA信号" -> {println("【转HDMI信号转换器】>> 转HDMI转化器将${signal}转化成HDMI数字信号")}"DP信号" -> {println("【转HDMI信号转换器】>> 转HDMI转化器将${signal}转化成HDMI数字信号")}"DVI信号" -> {println("【转HDMI信号转换器】>> 转HDMI转化器将${signal}转化成HDMI数字信号")}else ->{throw IllegalArgumentException("输入信号格式不支持")}}return "HDMI数字信号"}
总结
适配器模式可以以类适配器或对象适配器的形式实现,灵活性高。类适配器使用多重继承(在Java
中通过接口实现),对象适配器通过组合来实现,原本由于接口不兼容而无法一起工作的类可以协同工作,从而提高了代码的复用性,允许通过引入新的适配器来适应新需求,而无需修改现有的代码,从而实现了开闭原则。