【前端谈设计】适配器模式

155 阅读5分钟

前言

说到适配器,最先想到的是不是蓝牙适配器、Type-C转接头、手机头?有没有发现这些物件有一个共同点,那就是服务目标都是那些已经无法正常完成当前工作的物件。

比如手机头,一般的家用插座输出电压是220伏,但手机能接受的电压却是5伏,如果手机直接使用插座充电,那么手机也就会步入天堂!为了阻止这种事情的发生,所以就出现了手机头,手机头负责先将输出电压从200伏转换为5伏,再将转换后的电压用于手机正常充电,也就皆大欢喜了!!!

iPhone 13 高性价比「快充充电头」推荐—苹果手机快充充电器(Anker 安可、小米、绿联)

那程序中适配器的服务目标是那些喃?适配器又是如何去服务喃?那就开始今天的学习吧!!!

适配器模式

 将一个程序,转换成客户期望的另一个程序

适配器中主要有三个角色:

  • Target :目标程序
  • Adaptee :原始程序
  • Adapter :适配器

image-20230227202443763

适配器的关键就是不变与转换

使用适配器的场景一般发生在功能或需求发生改变的时,原始程序已经不能正常完成新功能,又想在不变原始程序的情况下去完成新功能,那就只能通过适配器转换原始程序生成目标程序,由目标程序去完成新功能

就像手机充电所需电压是5伏一样,在保证家庭电压不变的情况下,采用手机头转换电压的方式,可以实现手机正常充电

使用场景

在JAVA中适配器分类有:类适配器、接口适配器、对象适配器

TS中的适配器分类和JAVA中是一样的,而在JS中适配器分类少了接口适配器。其实只要符合代码不变和转换原则,那就符合适配器模式,所以在开发中不用过于较真概念,模式!==规则

对象适配器

场景:在日常前后端开发中,后端总是喜欢把布尔值存储为01,但是前端需要把布尔值显示到页面上,并且显示的文本应该是不知道知道,而不是01

首先肯定不能让后端那些渣渣们去改呀,因为他们会暴跳如雷(前端文章,应该没有后端会看)!!!那前端先尝试着改一下吧

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 天,点击查看活动详情