设计模式——适配器模式【结构型模式】(诸葛韩信总结版)

一、适配器模式

适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。

这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。举个真实的例子,读卡器是作为内存卡和笔记本之间的适配器。您将内存卡插入读卡器,再将读卡器插入笔记本,这样就可以通过笔记本来读取内存卡。

二、现实中的适配器

适配器在现实生活的应用非常广泛,接下来我们来看几个现实生活中的适配器模式。

港式插头转换器
电源适配器
USB转接口
电钻或者螺丝刀型号
水管口的拼接转换

三、优缺点

适配器的优缺点还是挺明显的。一般来说,代码在一开始的时候已经会考虑扩展,到后来进行修改的概率一般都不会比较大。而适配器就是为了解决两个代码不兼容的问题。如果不想代码变得难于维护,那就尽可能减少适配器的使用。

优点: 1、可以让任何两个没有关联的类一起运行。 2、提高了类的复用。 3、增加了类的透明度。 4、灵活性好。

缺点: 1、过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。 2、维护成本增加。

四、前端中常用适配器类型

在前端项目中,适配器模式的使用场景一般有以下三种情况:库的适配、参数的适配和数据的适配。下面我将以我在项目中的实际例子来说明。

五、适配器代码分析

1、参数的适配
参数的适配在函数中还是比较常见的一种方式,比如说,我们写了一个函数方法,但是方法在某些特定的条件下才需要某些参数,而另一些参数有可能不存在。那么我们可以在主函数中进行参数的判断或者默认。类也是相似处理的。
定义一个地方,需要传入一定的参数,才能够了解这个地方的具体内容,那么可以这么写

class Place {
	mainContent(location, populations, history, weather, sexRatio) {
		// doing something...
	}
}

我们将传入的参数整理成为一个对象,如下:

let enterObj = { // 该写法类似typescript的传参类型限制
	location: String, 
	populations: String, 
	history: String, 
	weather: ? String, 
	sexRatio: ? String
}

有可能我们不需要那么多的数据,那么我们可以判断一下

class Place {
	mainContent(obj) {
		let defaultObj = {
			location: '' , // 地理位置
			populations: '',  // 人口数量
			history: 'long',  // 历史长度
			weather: 'sunny',  // 天气
			sexRatio: '50%50%' // 性别比
		}
		defaultObj  = Object.assign(defaultObj , obj)
		// doing something...
	}
}

还有就是地图的互相切换:

var gaodeMap = {
        list: function(){
            console.log('呈现高德地图');
        }
    };

    var baiduMap = {
        showIt: function(){
            console.log('展示百度地图');
        }
    };

    var baiduMapAdapter = {
        list: function() {
            return baiduMap.showIt();
        }
    };

    var renderMap = function(map) {
        if(map.listinstanceof Function) {
            map.list();
        }
    };

    renderMap(gaodeMap);  //输出:呈现高德地图
    renderMap(baiduMapAdapter);    //输出:展示百度地图

在接口中,想展示什么接口都是可以的。以上是不想继续用高德地图而改用百度地图的方式。

2、库的适配
其实这个比较好理解,就是我们平时做项目的时候,有的时候,需要将某些库的方法改成另一个库的方法,或者是将某个库删除,然后设计到该库的函数统统转换成为其他的库或者自己手写的函数。
比如说,目前国内做得比较好的数据分析网站有百度统计、神策数据、友盟等。在一个你做的电商类网站 项目上线前,你的产品经理要求你接入了百度的代码用于数据采集,并在几十个涉及用户操作的地方进行了埋点。百度统计提供的埋点接口格式如下:

_hmt.push(['_trackEvent', category, action, opt_label,opt_value]);

按照产品经理的要求,你根据上面的格式将埋点代码写到了页面的多个地方:

//index.html
_hmt.push(['_trackEvent', 'web', 'page_enter', 'position', 'index.html']);

//product-detail.html
_hmt.push(['_trackEvent', 'web', 'page_enter', 'position', 'product-detail.html']);

_hmt.push(['_trackEvent', 'web', 'product_detail_view', 'product_id', productId]);

_hmt.push(['_trackEvent', 'web', 'add-product-chart', 'product_id', productId]);

//...还有几十个页面

过了几个月之后,该电商网站发展速度很快,运营人员感觉到百度统计提供的采集数据在已经无法满足当前网站的规模。运营人员和产品经理商量后决定,数据采集平台需要从百度统计切换到神策数据,神策数据提供的埋点接口格式如下:.

sa.track(eventName, {
  attrName: value 
})

接口的规则不同,就意味着你需要将几十个百度统计的_htm.push接口更改成为神策提供的sa.track接口。其实不用这么麻烦,写一个适配器就可以完成所有埋点事件的迁移:

//app.js

let _hmt = {
  push: (arr) {
    
    const [eventName, attrName, value] = [...arr.splice(2)];
    
    let attrObj = {
      [attrName]: value
    };

    sa.track(eventName, attrObj);
  }
}

通过分析比较百度统计的接口和神策的接口,可以知道在神策中只需要传入三个参数,eventName对应的是百度统计接口中的action, attrName对应的是百度统计接口中的opt_label, value对应的是百度统计接口中的opt_value; 删除了百度统计的SDK后,SDK所提供的_htm这个全局变量也就不存在了,我们可以利用该变量名做适配器,在push方法获取sa.track所需要的三个参数并调用sa.track即可。

3、数据的适配
这个相对简单很多,也就是将一些数据进行拆分或者组合,这里不做过多的解释。

参考:
1、菜鸟学院:https://www.runoob.com/design-pattern/adapter-pattern.html
2、从ES6重新认识JavaScript设计模式(四): 适配器模式https://segmentfault.com/a/1190000015482452
3、js适配器模式https://www.cnblogs.com/gongshunkai/p/6617388.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值