代碼也寫了幾年了,設計模式處于看了忘,忘了看的狀态,最近對設計模式有了點感覺,索性就再學習總結下吧。
大部分講設計模式的文章都是使用的
Java
、
C++
這樣的以類為基礎的靜态類型語言,作為前端開發者,
js
這門基于原型的動态語言,函數成為了一等公民,在實作一些設計模式上稍顯不同,甚至簡單到不像使用了設計模式,有時候也會産生些困惑。
下面按照「場景」-「設計模式定義」- 「代碼實作」- 「更多場景」-「總」的順序來總結一下,如有不當之處,歡迎交流讨論。
場景
當我們使用第三方庫的時候,常常會遇到目前接口和第三方接口不比對的情況,比如使用一個
Table
的元件,它要求我們傳回的表格資料格式如下:
{
code: 0, // 業務 code
msg: '', // 出錯時候的提示
data: {
total: , // 總數量
list: [], // 表格清單
}
};
複制
但後端傳回的資料可能是這樣的:
{
code: 0, // 業務 code
message: '', // 出錯時候的提示
data: {
total: , // 總數量
records: [], // 表格清單
}
};
複制
此時就可以通過擴充卡模式進行轉換。
擴充卡模式
看一下 維基百科 給的定義:
★In software engineering, the adapter pattern is a software design pattern that allows the interface of an existing class to be used as another interface.[1] It is often used to make existing classes work with others without modifying their source code.
”
通過擴充卡模式可以讓目前
class
不改變的情況下正常使用另一個
class
。
在以
class
為基礎的語言中有兩種實作方式,一種是通過組合的方式,擴充卡類内部包含原對象的執行個體。一種是通過類繼承,擴充卡類繼承原
class
。可以看下
UML
類圖:
image-20220213124112500
左邊的
Adapter
内部擁有
Adaptee
的執行個體,右邊的
Adapter
類直接繼承
Adaptee
類。
擴充卡會将
Adaptee
的
specificOperation
方法進行相應的處理包裝為
operation
方法供
client
使用。
看一個簡單的例子,現實生活中
iPhone
有兩種耳機插口,一種是
Lightning
,一種是傳統的
3.5
毫米接口。如果是
lightning
插口的耳機想要插到傳統的
3.5
毫米接口的電腦上就需要擴充卡了。
class Lightning耳機 {
public void 插入Lighting接口(){
System.out.println("插入到Lighting耳機接口成功");
}
}
class 傳統耳機 {
public void 插入到傳統耳機孔(){
System.out.println("插入到傳統耳機孔成功");
}
}
class Lightning耳機到傳統耳機擴充卡 extends 傳統耳機 {
public Lightning耳機 Lightning耳機;
public Lightning耳機到傳統耳機擴充卡(Lightning耳機 耳機) {
Lightning耳機 = 耳機;
}
public void 插入到傳統耳機孔(){
Lightning耳機.插入Lighting接口();
}
}
class 電腦傳統耳機孔 {
public 傳統耳機 耳機;
public 電腦傳統耳機孔(傳統耳機 傳統耳機) {
耳機 = 傳統耳機;
}
public void 插入耳機() {
耳機.插入到傳統耳機孔();
}
}
public class Main {
public static void main(String[] args) {
傳統耳機 傳統耳機 = new 傳統耳機();
電腦傳統耳機孔 電腦傳統耳機孔 = new 電腦傳統耳機孔(傳統耳機);
電腦傳統耳機孔.插入耳機(); // 插入到傳統耳機孔成功
Lightning耳機 Lightning耳機 = new Lightning耳機();
電腦傳統耳機孔 電腦傳統耳機孔2 = new 電腦傳統耳機孔(new Lightning耳機到傳統耳機擴充卡(Lightning耳機));
電腦傳統耳機孔2.插入耳機(); // 插入到Lighting耳機接口成功
}
}
複制
通過擴充卡我們成功将
Lightning
耳機插入到了電腦傳統耳機孔,讓我們再用
js
改寫一下。
const Lightning耳機 = {
插入Lighting接口(){
console.log("插入到Lighting耳機接口成功");
}
}
const 傳統耳機 = {
插入到傳統耳機孔(){
console.log("插入到傳統耳機孔成功");
}
}
const 電腦傳統耳機孔 = {
插入耳機(耳機) {
耳機.插入到傳統耳機孔();
}
}
const Lightning耳機到傳統耳機擴充卡 = function(Lightning耳機) {
return {
插入到傳統耳機孔(){
Lightning耳機.插入Lighting接口()
}
}
}
電腦傳統耳機孔.插入耳機(傳統耳機) // 插入到傳統耳機孔成功
電腦傳統耳機孔.插入耳機(Lightning耳機到傳統耳機擴充卡(Lightning耳機)) // 插入到Lighting耳機接口成功
複制
代碼實作
回到開頭接口不比對的問題上,
Table
元件提供了一個
responseProcessor
的鈎子,我們隻需要通過這個鈎子将接口傳回的資料進行包裝即可。
{
...
responseProcessor(res) {
return {
...res,
msg: res.message, // 出錯時候的提示
data: {
...res.data
list: res?.data?.records || [], // 表格清單
}
};
},
...
}
複制
更多場景
除了應對資料格式不一緻的問題,通過擴充卡模式我們還可以為上層提供統一接口,來解決相容性問題。最典型的例子就是
jQuery
,可以看一下其中一段代碼:
// Create the request object
// (This is still attached to ajaxSettings for backward compatibility)
jQuery.ajaxSettings.xhr = window.ActiveXObject !== undefined ?
// Support: IE6-IE8
function() {
// XHR cannot access local files, always use ActiveX for that case
if ( this.isLocal ) {
return createActiveXHR();
}
// Support: IE 9-11
// IE seems to error on cross-domain PATCH requests when ActiveX XHR
// is used. In IE 9+ always use the native XHR.
// Note: this condition won't catch Edge as it doesn't define
// document.documentMode but it also doesn't support ActiveX so it won't
// reach this code.
if ( document.documentMode > 8 ) {
return createStandardXHR();
}
// Support: IE<9
// oldIE XHR does not support non-RFC2616 methods (#13240)
// See http://msdn.microsoft.com/en-us/library/ie/ms536648(v=vs.85).aspx
// and http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9
// Although this check for six methods instead of eight
// since IE also does not support "trace" and "connect"
return /^(get|post|head|put|delete|options)$/i.test( this.type ) &&
createStandardXHR() || createActiveXHR();
} :
// For all other browsers, use the standard XMLHttpRequest object
createStandardXHR;
複制
易混設計模式
擴充卡模式和代理模式在代碼結構上很像,代理模式也是對原對象進行包裝處理。差別在于它們的意圖不同:
- 擴充卡模式是為了解決兩個對象之間不比對的問題,而原對象又不适合直接修改,此時可以使用擴充卡模式進行一層轉換。
- 代理模式是為了增強原對象的功能,提供的接口不會改變。
總
擴充卡模式是一種比較簡單的設計模式,在
js
中也會很自然的應用,一般通過一個函數進行轉換即可。