适配器模式
# 1. 适配器模式简介
# 模式引入
【生活案例】:我们在给手机充电时,连接到插座上的那个,它的学名就称之为”适配器”。其原理和我们这里所说的适配器是一样的道理,用一张图来形容就是:
适配器就是将一种接口转换成另一种接口并且不改变其功能的正常性
ClassA 的methodA 方法直接调用ClassB 的methodB 方法,但是由于参数不匹配,无法直接调用;
中间加个适配器(Adapter), 将ClassB的接口适配成ClassA所期待的,通过适配器来调整methodB的参数改为methodA所匹配的。一个适配允许通常因为接口不兼容而不能在一起工作的类工作在一起,做法是将类自己的接口包裹在一个已存在的类中.
# 基本介绍
- 适配器模式(Adapter Pattern)将某个类的接口转换成客户端期望的另一个接口表示,主的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。其别名为包装器(Wrapper)
- 适配器模式属于结构型模式
- 主要分为三类:类适配器模式、对象适配器模式、接口适配器模式
# 2. Adapter的工作原理
- 适配器模式:将一个类的接口转换成另一种接口.让原本接口不兼容的类可以兼容
- 从用户的角度看不到被适配者,是解耦
- 用户调用适配器转化出来的目标接口方法,适配器再调用被适配者的相关接口方法
- 用户收到反馈结果,感觉只是和目标接口交互,如图
- 目标(Target)角色:客户端所期待得到的接口
- 适配器(Adaper)角色:适配器类是本模式的核心。适配器把源接口转换成目标接口。显然,这一角色不可以是接口,而必须是具体类。
- 源(Adapee)角色被适配者:现在需要被适配的接口
# 3. 类适配器模式
类适配器模式是通过继承来实现适配功能的,代码如下:
我们先来定义一个源角色(即220V电压)
open class Voltage220V {
fun output220V(): Int {
val src: Int = 220
println("电压 ==> $src 伏")
return src
}
}
2
3
4
5
6
7
然后我们再来定义一个目标角色(即通过适配从而获取到的5V电压),这个地方我们定义的是一个接口
interface IVoltage5V {
fun output5V(): Int
}
2
3
然后,我们再来定义一个适配器,适配器的作用是将220V电压转换成为5V电压。我们看看适配器的代码
class VoltageAdapter : Voltage220V(), IVoltage5V {
override fun output5V(): Int {
// 获取220V电压
val src: Int = output220V()
// 返回5V电压
return src / 44
}
}
2
3
4
5
6
7
8
这里,我们的适配器,继承于源角色并且实现目标角色,这样通过实现目标角色中的方法调用源角色中的方法进行运算,从而达到适配的效果。我们编写一个测试类看看
fun main(args: Array<String>) {
println("===== > 适配器模式 < =====")
val phone = Phone()
phone.charging(VoltageAdapter())
}
2
3
4
5
运行结果:
===== > 适配器模式 < =====
电压 ==> 220 伏
电压为5V, 可以从充电
2
3
从代码中我们可以看到,其实适配器做的主要工作就是为了让目标角色的API可以调用到源角色的API,适配器在中间做的是一个类似的中转作用,并且不影响源角色和目标角色原有的功能和逻辑。
# 4. 对象适配器
对象适配器是通过组合来实现适配器功能的,即适配器拥有源角色的实例,我们使用代码来看看:
此处源角色和目标角色两个类代码和上面是一样的,不另做介绍,我们看看适配器角色代码
// Kotlin中主构造方法就是直接跟在类名后面的
class VoltageAdapter(private var voltage220V: Voltage220V) : IVoltage5V {
override fun output5V(): Int {
// 获取220V电压
val src: Int = voltage220V.output220V()
val target = src / 44
println("适配完成,输出的电压为$target")
// 返回5V电压
return target
}
}
2
3
4
5
6
7
8
9
10
11
12
如上代码所示,我们的适配器中有一个有参构造,参数为源角色对象实例,适配器中有源角色对象实例引用,通过对象的引用我们进行适配转换。我们编写一个测试类看看
fun main(args: Array<String>) {
println("===== > 对象适配器模式 < =====")
val phone = Phone()
phone.charging(VoltageAdapter(Voltage220V()))
}
2
3
4
5
运行结果:
===== > 对象适配器模式 < =====
电压 ==> 220 伏
适配完成,输出的电压为5
电压为5V, 可以充电
2
3
4
# 6. 接口适配器模式
接口适配器相对类适配器和对象适配器而言,接口适配器相对更加灵活,就好比手机适配器中的万能适配器,不管接入的是多少伏的电源,最终都能保证输出电源为5V。
首先,我们先来定义一个抽象的电源,并且给予一个默认值为220V
abstract class ACV {
open fun output(): Int {
return 220
}
}
2
3
4
5
然后我们再来定义具体的220V的电源
open class AC220V : ACV() {
override fun output(): Int {
return 220
}
}
2
3
4
5
我们再定义一个具体的110V电源,和220V电源一样,继承于抽象的电源类
class AC110V : ACV() {
override fun output(): Int {
return 110
}
}
2
3
4
5
这样我们定义好了源角色,我们再来定义目标角色(最终适配输出的target)
interface DC5V {
fun dc5V(): Int
}
2
3
定义适配器
在适配器角色中,我们定义一个抽象的电源,并且提供多个适配器角色的有参构造,通过具体源角色的实例使用抽象的电源引用,适配器类实现于目标角色并实现目标角色的方法,在方法体中,我们进行逻辑处理,将输入的电压进行适配为5V电压,从而达到万能适配的效果。
这样,不管输入的电压为多少,都能做到有效适配为最终想要的结果。
class Adapter : DC5V {
private var acv: ACV?
constructor(ac220V: AC220V?) {
acv = ac220V
}
constructor(ac110V: AC110V?) {
acv = ac110V
}
override fun dc5V(): Int {
var ac = 0
if (acv != null) {
ac = acv!!.output()
}
val sta = ac / 5
return ac / sta
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
测试类
在测试类中,我们提供具体的源角色,然后交由适配器进行适配
fun main(args: Array<String>) {
println("===== > 接口适配器模式 < =====")
val dc5V: DC5V = Adapter(AC220V())
val dc: Int = dc5V.dc5V()
println("输入的电压为:" + AC220V().output() + " 伏...")
println("转换后的电压为:$dc 伏...")
}
2
3
4
5
6
7
输出结果
===== > 接口适配器模式 < =====
输入的电压为:220 伏...
转换后的电压为:5 伏...
2
3
如上就是适配器模式的三种适配形式,前两种适配器相对差别比较小,仅仅是在适配器阶段有所区别,但是接口适配器相对区别就比较大了。但是其目的都是一样的,就是将原本不匹配的两者变的匹配并且不影响原有的逻辑结构。就像我们平时看到的转换器。