天天看點

javascript設計模式——擴充卡模式

[1]現實中的擴充卡

[2]應用

[3]總結

前面的話

  擴充卡模式的作用是解決兩個軟體實體間的接口不相容的問題。使用擴充卡模式之後,原本由于接口不相容而不能工作的兩個軟體實體可以一起工作。擴充卡的别名是包裝器(wrapper),這是一個相對簡單的模式。在程式開發中有許多這樣的場景:當試圖調用子產品或者對象的某個接口時,卻發現這個接口的格式并不符合目前的需求。這時候有兩種解決辦法,第一種是修改原來的接口實作,但如果原來的子產品很複雜,或者拿到的子產品是一段别人編寫的經過壓縮的代碼,修改原接口就顯得不太現實了。第二種辦法是建立一個擴充卡,将原接口轉換為客戶希望的另一個接口,客戶隻需要和擴充卡打交道。本文将詳細介紹擴充卡模式

現實中的擴充卡

  擴充卡在現實生活的應用非常廣泛,接下來來看幾個現實生活中的擴充卡模式

  1、港式插頭轉換器

  港式的電器插頭比大陸的電器插頭體積要大一些。如果從香港買了一個Macbook,會發現充電器無法插在家裡的插座上,為此而改造家裡的插座顯然不友善,是以需要一個擴充卡:

  2、電源擴充卡

  Macbook電池支援的電壓是20V,日常生活中的交流電壓一般是220V。除了了解的220V交流電壓,日本和南韓的交流電壓大多是100V,而英國和澳洲的是240V。筆記本電腦的電源擴充卡就承擔了轉換電壓的作用,電源擴充卡使筆記本電腦在100V~240V的電壓之内都能正常工作,這也是它為什麼被稱為電源“擴充卡”的原因

  3、USB轉接口

  在以前的電腦上,PS2接口是連接配接滑鼠、鍵盤等其他外部裝置的标準接口。但随着技術的發展,越來越多的電腦開始放棄了PS2接口,轉而僅支援USB接口。是以那些過去生産出來的隻擁有PS2接口的滑鼠、鍵盤、遊戲搖桿等,需要一個USB轉接口才能繼續正常工作,這是PS2-USB擴充卡誕生的原因

應用

  如果現有的接口已經能夠正常工作,那就永遠不會用上擴充卡模式。擴充卡模式是一種“亡羊補牢”的模式,沒有人會在程式的設計之初就使用它。因為沒有人可以完全預料到未來的事情,也許現在好好工作的接口,未來的某天卻不再适用于新系統,那麼可以用擴充卡模式把舊接口包裝成一個新的接口,使它繼續保持生命力。比如在JSON格式流行之前,很多cgi傳回的都是XML格式的資料,如果今天仍然想繼續使用這些接口,顯然可以創造一個XML-JSON的擴充卡

  下面是一個執行個體,向googleMap和baiduMap都發出“顯示”請求時,googleMap和baiduMap分别以各自的方式在頁面中展現了地圖:

var googleMap = {
    show: function(){
        console.log( '開始渲染谷歌地圖' );
    }
};
var baiduMap = {
    show: function(){
        console.log( '開始渲染百度地圖' );
    }
};
var renderMap = function( map ){
    if ( map.show instanceof Function ){
        map.show();
    }
};

renderMap( googleMap ); // 輸出:開始渲染谷歌地圖
renderMap( baiduMap ); // 輸出:開始渲染百度地圖      

  這段程式得以順利運作的關鍵是googleMap和baiduMap提供了一緻的show方法,但第三方的接口方法并不在控制範圍之内,假如baiduMap提供的顯示地圖的方法不叫show而叫display呢?

  baiduMap這個對象來源于第三方,正常情況下都不應該去改動它。此時可以通過增加baiduMapAdapter來解決問題:

var googleMap = {
    show: function(){
        console.log( '開始渲染谷歌地圖' );
    }
};
var baiduMap = {
    display: function(){
        console.log( '開始渲染百度地圖' );
    }
};
var baiduMapAdapter = {
    show: function(){
        return baiduMap.display();

    }
};

renderMap( googleMap ); // 輸出:開始渲染谷歌地圖
renderMap( baiduMapAdapter ); // 輸出:開始渲染百度地圖      

  再看看另一個例子。假設正在編寫一個渲染北京市地圖的頁面。目前從第三方資源裡獲得了北京市的所有地區以及它們所對應的ID,并且成功地渲染到頁面中:

var getBeijingCity = function(){
    var beijingCity = [
    {
        name: 'chaoyang',
        id: 11,
    }, {
        name: 'haidian',
        id: 12,
    }
    ];
    return beijingCity;
};
var render = function( fn ){
    console.log( '開始渲染北京市地圖' );
    document.write( JSON.stringify( fn() ) );
};
render( getBeijingCity );      

  利用這些資料,編寫完成了整個頁面,并且線上上穩定地運作了一段時間。但後來發現這些資料不太可靠,裡面還缺少很多地區。于是又在網上找到了另外一些資料資源,這次的資料更加全面,但遺憾的是,資料結構和正運作在項目中的并不一緻。新的資料結構如下:

var BeijingCity = {
    chaoyang: 11,
    haidian: 12,
    pinggu: 13
};      

  除了大動幹戈地改寫渲染頁面的前端代碼之外,另外一種更輕便的解決方式就是新增一個資料格式轉換的擴充卡:

var getBeijingCity = function(){
    var beijingCity = [
    {
        name: 'chaoyang',
        id: 11,
    }, {
        name: 'haidian',
        id: 12,
    }

    ];
    return beijingCity;
};
var render = function( fn ){
    console.log( '開始渲染北京市地圖' );
    document.write( JSON.stringify( fn() ) );
};
var addressAdapter = function( oldAddressfn ){
    var address = {},
    oldAddress = oldAddressfn();
    for ( var i = 0, c; c = oldAddress[ i++ ]; ){
        address[ c.name ] = c.id;
    }
    return function(){
        return address;
    }
};
render( addressAdapter( getBeijingCity ) );      

  那麼接下來需要做的,就是把代碼中調用getBeijingCity的地方,用經過addressAdapter擴充卡轉換之後的新函數來代替

總結

  擴充卡模式是一對相對簡單的模式。有一些模式跟擴充卡模式的結構非常相似,比如裝飾者模式、代理模式和外觀模式。這幾種模式都屬于“包裝模式”,都是由一個對象來包裝另一個對象。差別它們的關鍵仍然是模式的意圖。擴充卡模式主要用來解決兩個已有接口之間不比對的問題,它不考慮這些接口是怎樣實作的,也不考慮它們将來可能會如何演化。擴充卡模式不需要改變已有的接口,就能夠使它們協同作用。裝飾者模式和代理模式也不會改變原有對象的接口,但裝飾者模式的作用是為了給對象增加功能。裝飾者模式常常形成一條長的裝飾鍊,而擴充卡模式通常隻包裝一次。代理模式是為了控制對對象的通路,通常也隻包裝一次。外觀模式的作用倒是和擴充卡比較相似,有人把外觀模式看成一組對象的擴充卡,但外觀模式最顯著的特點是定義了一個新的接口

好的代碼像粥一樣,都是用時間熬出來的

javascript設計模式——擴充卡模式