適配器模式(Adapter):將一個類的接口轉換成客戶但願的另一個接口,使得本來因爲接口不兼容而不能一塊兒工做的那些類能夠一塊兒工做。
在生活中,咱們有許多的適配器,例如iPhone7之後的耳機接口從3.5mm圓孔接口更改爲爲了蘋果專屬的 lightning接口。許多人之前的圓孔耳機就須要下面的一個適配器,纔可以在自個兒新買的iPhone上面聽歌。html
在前端開發中,咱們可能會碰見這樣的場景:當咱們試圖調用某個模塊或者對象的接口時,卻發現這個接口的格式不符合咱們的需求。這時有兩種解決辦法:第一種是修改原來的接口實現,但若是原來的代碼很複雜,例如是一個庫或框架,更改原代碼就顯得很不現實了。因此這時就須要使用今天所講的第二種辦法:建立一個適配器,將原接口轉換爲客戶但願的另外一個接口,客戶只須要使用適配器便可。前端
對於只有JavaScript這一門語言經驗的前端開發來講,可能對於接口的概念比較陌生。建議參考閱讀TypeScript-接口的文檔來更好的理解接口。git
在前端項目中,適配器模式的使用場景通常有如下三種狀況:庫的適配、參數的適配和數據的適配。下面我將以我在項目中的實際例子來講明。web
項目上線前一般會要求前端開發者在頁面中會接入統計網頁數據用的SDK,這些SDK可以採集用戶的信息和網頁行生成可視化的圖表和表格,來幫助網站運營人員和產品經理更好的根據用戶行爲來提高網頁質量。咱們來看一下適配器在接入採集數據的庫時的使用場景:ajax
目前國內作得比較好的數據分析網站有百度統計、神策數據、友盟等。在一個你作的電商類網站 項目上線前,你的產品經理要求你接入了百度的代碼用於數據採集,並在幾十個涉及用戶操做的地方進行了埋點。百度統計提供的埋點接口格式以下:後端
_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
便可。app
有的狀況下一個方法可能須要傳入多個參數,例如在SDK
這個類中有一個phoneStatus
,須要傳入五個參數用於接收手機的相關信息:
class SDK { phoneStatus(brand, os, carrier, language, network) { //dosomething..... } }
一般在傳入的參數大於3的時候,咱們就能夠考慮將參數合併爲一個對象的形式,就像咱們$.ajax
的作法同樣。下面咱們能夠將phoneStatus
的參數接口定義以下(String
表明參數類型,?:
表明可選項)
{ brand: String os: String carrier:? String language:? String network:? String }
能夠看出,carrier
、language
,network
這三個屬性不是必須傳入的,它們在方法內部可能被設置一些默認值。因此這個時候咱們就能夠在方法內部採用適配器來適配這個參數對象。
class SDK { phoneStatus(config) { let defaultConfig = { brand: null, //手機品牌 os: null, //系統類型: Andoird或 iOS carrier: 'china-mobile', //運營商,默認中國移動 language: 'zh', //語言類型,默認中文 network: 'wifi', //網絡類型,默認wifi } //參數適配 for( let i in config) { defaultConfig[i] = config[i] || defaultConfig[i]; } //dosomething..... } }
數據的適配在前端中是最爲常見的場景,這時適配器在解決先後端的數據依賴上有着重要的意義。一般服務器端傳遞的數據和咱們前端須要使用的數據格式是不一致的,特別是在在使用一些UI框架時,框架所規定的數據有着固定的格式。因此,這個時候咱們就須要對後端的數據格式進行適配。
例如網頁中有一個使用Echarts折線圖對網站每週的uv
,一般後端返回的數據格式以下所示:
[ { "day": "週一", "uv": 6300 }, { "day": "週二", "uv": 7100 }, { "day": "週三", "uv": 4300 }, { "day": "週四", "uv": 3300 }, { "day": "週五", "uv": 8300 }, { "day": "週六", "uv": 9300 }, { "day": "週日", "uv": 11300 } ]
可是Echarts須要的x軸的數據格式和座標點的數據是長下面這樣的:
["週二", "週二", "週三", "週四", "週五", "週六", "週日"] //x軸的數據 [6300. 7100, 4300, 3300, 8300, 9300, 11300] //座標點的數據
因此這是咱們就可使用一個適配器,將後端的返回數據作適配:
//x軸適配器 function echartXAxisAdapter(res) { return res.map(item => item.day); } //座標點適配器 function echartDataAdapter(res) { return res.map(item => item.uv); }
適配器模式在JS中的使用場景不少,在參數的適配上,有許多庫和框架都使用適配器模式;數據的適配在解決先後端數據依賴上十分重要。可是適配器模式本質上是一個亡羊補牢的模式,它解決的是現存的兩個接口之間不兼容的問題,你不該該在軟件的初期開發階段就使用該模式;若是在設計之初咱們就可以統籌的規劃好接口的一致性,那麼適配器就應該儘可能減小使用。