适配器模式
基本概念
-
适配器:就是使得一个东西适合另外一个东西的东西。 -
定义:适配器模式(Adapter),将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。简单地概括,就是需要的东西在眼前,但是不能使用,短时间又无法改造它,于是我们就通过适配器来适配它适合我们使用。 -
分类:
- 类适配器模式: 通过
多重继承对一个接口与另外一个接口进行匹配。 - 对象适配器模式:
由于
Java/C#/VB.NET语言,只支持了类的单继承(C++可支持类的多继承),所以下面的都是主讲对象适配器。
基本结构和代码
UML图
实现代码
Target 客户所期待的接口(可以是具体/抽象类或接口)
/**
* @create on 2020/6/14 14:13
* @description 目标类,这是客户所期待的接口或(具体/抽象)类。
* @author mrdonkey
*/
open class Target {
open fun request() {
println("普通请求!")
}
}
Adaptee需要适配的类
/**
* @create on 2020/6/14 14:16
* @description 需要是适配的类
* @author mrdonkey
*/
class Adaptee {
fun specificRequest(){
println("特殊请求!")
}
}
Adapter 适配器类
/**
* @create on 2020/6/14 14:16
* @description 适配器类(目标类的实现类),通过在内部包装一个需要适配的类Adaptee对象 ,把源接口转成目标接口。
* 简单的来说,就是当目标类调用指定的方法时,内部实现执行适配的方法,达到适配的效果。
* @author mrdonkey
*/
class Adapter : Target() {
private val adaptee = Adaptee()//建立一个私有的Adaptee对象
override fun request() {
adaptee.specificRequest()//表面是Target的request方法,实际变成调用了想要适配的类的方法。
}
}
Client 客户端
/**
* @create on 2020/6/14 14:19
* @description 客户端测试
* @author mrdonkey
*/
class Client {
companion object {
@JvmStatic
fun main(args: Array<String>) {
val target = Adapter()
target.request()//达到adaptee能适合Target使用的效果。
}
}
}
测试结果:
特殊请求!
何时应用
- 希望复用一些现存的一些类,但是接口又和复用环境不一致的情况。
- 两个类所做的事情相似,但是具备不同接口,首先考虑通过重构统一接口;若是双方都是不太容易修改的再使用适配器模式适配,而不是一有不同时就使用。
- 进行设计前预防接口不同问题发生;在有小的接口不统一时,及时重构;碰到无法改变原有的设计和代码的情况,才考虑使用适配器适配。
NBA小案例
-
设计一场简单的球赛有前峰、中锋、后卫,他们都有一个action行为接收英文命令command。
-
UML图
Player 球员抽象类
/**
* @create on 2020/6/14 14:24
* @description 抽象球员类
* @author mrdonkey
*/
abstract class Player {
/**
* 拥有一个接受命令而行动的方法
*/
abstract fun action(command: String)
}
Forwards 前锋
/**
* @create on 2020/6/14 14:27
* @description 前锋
* @author mrdonkey
*/
class Forwards : Player() {
override fun action(command: String) {
println("${this.javaClass.simpleName}----->$command")
}
}
Center中锋和Guards后卫类似。
Client 赛场
/**
* @create on 2020/6/14 14:30
* @description 球场
* @author mrdonkey
*/
class Client {
companion object {
@JvmStatic
fun main(args: Array<String>) {
val forwards = Forwards()
forwards.action("attack")//收到进攻命令
val center = Center()
center.action("attack")
val guards = Center()
guards.action("defense")//收到防守命令
}
}
}
测试结果:
Forwards----->attack
Center----->attack
Center----->defense
-
假如在这场比赛中加入一个只会中文的中国中锋球员
ChinaCenter,他听不懂英语也就无法通过接受英文的command进行行动,为了适配ChinaCenter这个类,我们需要创建一个翻译者Translator来适配,让ChinaCenter能够加入到球赛中。 -
应用适配器模式的UML图

ChinaCenter 中国中锋球员类
为什么他不继承于Player类?我们在这里假设中国中锋这类是无法轻易更改的,即便继承于它,由于中锋球员是无法听懂英语command的,那就没办法进行比赛了,所以需要一个翻译者来进行翻译(适配),让它也能够加入赛场,这个例子不够严谨,仅是为了演示适配器模式的一种应用。
/**
* @create on 2020/6/14 14:35
* @description 中国中锋球员 只会中文
* @author mrdonkey
*/
class ChinaCenter {
fun action(command: String) {
println("${this.javaClass.simpleName}----->$command")
}
}
Translator 翻译者 作为ChinaCenter的适配器
/**
* @create on 2020/6/14 14:33
* @description 翻译者(中国球员的适配器,让中国球员能够听得懂) 为中国球员提供翻译
* @author mrdonkey
*/
class Translator : Player() {
private val chinaCenter = ChinaCenter()//表明翻译者与中国中锋球员有关联
/**
* s
*/
override fun action(command: String) {
chinaCenter.action(//告诉中国中锋
when (command) {
"attack" -> {//翻译进攻
"进攻"
}
"defense" -> {//翻译防守
"防守"
}
else -> {
""
}
}
)
}
}
中国中锋加入球赛
Client 赛场
class Client {
companion object {
@JvmStatic
fun main(args: Array<String>) {
//...省略
val chinaPlayer = Translator()
chinaPlayer.action("attack")
}
}
}
测试结果:
ChinaCenter----->进攻