前言
说到适配器,最先想到的是不是蓝牙适配器、Type-C转接头、手机头?有没有发现这些物件有一个共同点,那就是服务目标都是那些已经无法正常完成当前工作的物件。
比如手机头,一般的家用插座输出电压是220伏,但手机能接受的电压却是5伏,如果手机直接使用插座充电,那么手机也就会步入天堂!为了阻止这种事情的发生,所以就出现了手机头,手机头负责先将输出电压从200伏转换为5伏,再将转换后的电压用于手机正常充电,也就皆大欢喜了!!!
那程序中适配器的服务目标是那些喃?适配器又是如何去服务喃?那就开始今天的学习吧!!!
适配器模式
将一个程序,转换成客户期望的另一个程序
适配器中主要有三个角色:
Target:目标程序Adaptee:原始程序Adapter:适配器
适配器的关键就是不变与转换
使用适配器的场景一般发生在功能或需求发生改变的时,原始程序已经不能正常完成新功能,又想在不变原始程序的情况下去完成新功能,那就只能通过适配器转换原始程序生成目标程序,由目标程序去完成新功能
就像手机充电所需电压是5伏一样,在保证家庭电压不变的情况下,采用手机头转换电压的方式,可以实现手机正常充电
使用场景
在JAVA中适配器分类有:类适配器、接口适配器、对象适配器
TS中的适配器分类和JAVA中是一样的,而在JS中适配器分类少了接口适配器。其实只要符合代码不变和转换原则,那就符合适配器模式,所以在开发中不用过于较真概念,模式!==规则
对象适配器
场景:在日常前后端开发中,后端总是喜欢把布尔值存储为0和1,但是前端需要把布尔值显示到页面上,并且显示的文本应该是不知道和知道,而不是0和1。
首先肯定不能让后端那些渣渣们去改呀,因为他们会暴跳如雷(前端文章,应该没有后端会看)!!!那前端先尝试着改一下吧
let result = {
isOk: 1
}
result.isOk === 1 ? '知道' : '不知道'
let finalResult = result
复制代码
上面这种实现虽然也可以得到finalResult对象,但影响到result对象,并且如果以后result对象其他数据也需要处理,又在下面加代码吗?显然不合理!
let result = {
isOk: 1,
sex: 1
}
function resultTarget(result) {
let finalResult = { ...result }
finalResult.isOk === 1 ? '知道' : '不知道'
finalResult.sex === 1 ? '男' : '女'
return finalResult
}
let finalResult = resultTarget(result)
复制代码
这一次的实现方式是不是就避免了上面的问题了?
result对象到finalResult对象的转换并不会影响result对象- 在添加其他数据处理需求时,直接修改
resultTarget函数就可以 resultTarget函数是可以被其他地方代码复用
类适配器
例子:又来看看手机充电的例子
因为JS中大部分都是对象转换器,所以这里通过TS来描述电压转换
interface Exportation {
output(): number;
}
class Family implements Exportation {
output() {
return 220
}
}
class Phone {
charge(exportation: Exportation) {
if (exportation.output() > 5) {
console.log("要爆炸啦!!!");
return;
}
console.log("正常充电中!!!");
}
}
let phone = new Phone()
let family = new Family()
phone.charge(family) // 要爆炸啦!!!
复制代码
直接充电显然是不行的啦,还是请出充电头
interface ExportationTarget {
output(): number;
}
interface Exportation {
output(): number;
}
class Family implements Exportation {
output() {
let vNum = 220
return vNum
}
}
class Head extends Family implements ExportationTarget {
output() {
console.log("==========开始转换==========");
let vNum = super.output() / 45
return vNum
}
}
class Phone {
charge(exportation: ExportationTarget) {
if (exportation.output() > 5) {
console.log("要爆炸啦!!!");
return;
}
console.log("正常充电中!!!");
}
}
let phone = new Phone()
let family = new Head()
phone.charge(family) // 正常充电中!!!
复制代码
通过Head类继承Exportation的方式适配转换,并且Family类也并没有发生任何改变
对象适配器采用组合方式实现适配,而类转换器采用的是继承方式
接口适配器(了解)
例子:小程序需要许多登录方式(QQ、WX、账号密码)
interface Login {
wxLogin(): void
qqLogin(): void
passLogin(): void
}
复制代码
在功能开发初,项目经理定义登录登功能接口,但是接口内部包含了许多登录方法,如果每次实现接口时都要实现这些方法,那将增加许多工作量,为了解决这个问题,那就要搬出接口适配器
interface Login {
wxLogin(): void
qqLogin(): void
passLogin(): void
}
/**
* 接口适配器,实现Login接口并且实现Login接口所以方法
*/
abstract class LoginAdapter implements Login {
wxLogin(): void { }
qqLogin(): void { }
passLogin(): void { }
}
class WxLoginTarget extends LoginAdapter {
wxLogin() {
console.log("微信登录");
}
}
class QqLoginTarget extends LoginAdapter {
qqLogin() {
console.log("qq登录");
}
}
复制代码
适配器的优缺点
优点
- 在不改变原始程序的情况下,也能实现新的功能
- 适配器和原始程序是相互隔离的,有利于适配器扩展
- 适配器是可复用的
缺点
- 增加适配器代码,也就增加了代码的复杂性,降低了代码可读性
- 多个适配器也并不适合共存,使用要适量
开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 25 天,点击查看活动详情