看一年前在當當網所買,卻還嶄新的那本《JavaScript設計模式》(Rose Harmes、Dustin Diaz 著,謝廷晟 譯,人民郵電出版社出版),作者將GoF合著的《Design Patterns》一書中若幹設計模式在JavaScript中的應用給瞭詳盡的解說,不失為一本好書。
我比較喜歡的設計模式有工廠模式、適配器模式、組合模式和觀察者模式等,本文總結書中關於適配器模式的講解,希望該模式能為讀者在JavaScript開發中所用。
1、 什麼是適配器模式?
在計算機編程中,適配器模式將一個類的接口適配成用戶所期待的。一個適配允許通常因為接口不兼容而不能在一起工作的類工作在一起,做法是將類自己的接口包裹在一個已存在的類中。如果現有的接口能很好地滿足需要,那就可能沒有必要使用適配器。
從表面上看,適配器模式很像門面模式。它們都要對別的對象進行包裝並改變其呈現的接口。兩者的區別在於它們如何改變接口。門面元素展現的是一個簡化的接口,它並不提供額外的選擇,而且有時為瞭方便完成任務它還要做出某些假定。而適配器則要把一個叫接口轉換成另外一個接口,它並不會濾除某些能力,也不會簡化接口。
2、 適配器模式的應用場合
下面舉幾個適合使用適配器模式的實例:
(1) 參數類型不一致: 假設有這樣一個對象,定義如下:
var clientObject = {
string1: 'foo',
string2: 'bar',
string3: 'baz'
};
另外有一個以三個字符串為參數的函數:
function interfaceMethod(str1, str2, str3) {
}
為瞭將cientObject作為參數傳遞給interfaceMethod方法,可使用適配器模式解決:
function clientToInterfaceAdapter(o) {
interfaceMethod(o.string1, o.string2, o.string3);
}
現在可以將clientObject對象作為參數傳給clientToIntefaceAdapter方法:
clientToIntefaceAdapter(clientObject);
使用適配器模式節解決瞭參數類型有些許不一致造成的問題。
(2)PS2-to-USB適配器:我比較喜歡這個關於適配器的例子,很好理解適配器的作用。 以PC硬件為例,PS2插口是連接鼠標和鍵盤的標準接口。多年以來幾乎所有PC都帶有這種接口,鼠標和鍵盤的設計人員因此就有瞭一個固定的設計目標。後來硬件工程師發明瞭可以完全替代PS2接口的技術,這種設計改用USB系統來支持鍵盤、鼠標和其他外設。
但是問題出現瞭,對於設計主板的工程師來說,消費者有沒有USB鍵盤都無所謂,他們決定不再支持PS2插口,以便降低成本和節省空間。但是鍵盤和鼠標的設計者要想賣掉以前針對PS2接口成產的成千上萬的鍵盤和鼠標,需要有適配器的支持才行,PS2-to-USB應運而生。
3、 在JavaScript中使用適配器模式
現在可供選擇的庫非常多,看看哪套工具最適合自己的需要,以及它們對自己的開發會帶來什麼影響。此外還要考慮開發人員的編程風格、實現難易程度和兼容性等等問題。即使以前選定瞭庫,但可能出於性能、安全或設計等方面的考慮,可能會更換所用的庫。
那要怎麼樣才能實現兩個庫之間的平滑過渡呢?此時可以使用適配器模式從一套API過渡到另一套API。例如在下面的實例中實現的是從Prototype庫的$函數到YUI(Yahoo!User Interface)的get方法的轉換。這兩個函數功能相似,但接口方面有些區別。首先看看Prototype的$函數:
function $() {
var elements = new Array();
for (var i = 0; i < arguments.length; i++) {
var element = arguments[i];
if (typeof element == 'string') {
element = document.getElementById(element);
}
if (arguments.length == 1) {
return element;
}
elements.push(element);
}
return elements;
}
YUI(Yahoo!User Interface)的get方法的代碼如下:
YAHOO.util.Dom.get = function() {
if (YAHOO.lang.isString(el)) {
return document.getElementById(el);
}
if (YAHOO.lang.isArray(el)) {
var c = [];
for (var i = 0, len = el.length; i < len; ++i) {
c[c.length] = YAHOO.util.Dom.get(el[i]);
}
return c;
}
if (el) {
return el;
}
return null;
};
兩者的區別:get具有一個參數,這個參數可以是一個HTML元素、字符串或由字符串或HTML元素組成的數組。而$函數沒有正式列出參數,而是允許用戶傳入任意數目的參數,可以是字符串或HTML元素。
如果想要Prototype的$函數改為使用YUI的get方法,可使用如下這個適配器:
function PrototypeToYUIAdapter() {
return YAHOO.util.Dom.get(arguments);
}
隻要添加如下代碼後,在Prototype代碼庫被替換YAHOO後,$方法可以照用:
$= PrototypeToYUIAdapter;
將YAHOO的get方法適配成Prototype的$方法的適配器如下:
function YUIToPrototypeAdapter(el) {
return $.apply(window, el instanceof Array ? el : [el]);
}
隻要添加如下代碼後,在YAHOO代碼庫被替換Prototype後,get方法可以照用:
YAHOO.util.Dom.get = YUIToPrototypeAdapter;
4、 適配器模式的優缺點
適配器最大的優點是可以避免大規模的改寫現有客戶代碼。
如果現有API還未定形,或新接口還未定形,此時並不適合使用適配器模式。
5、 參考文檔
(1)《JavaScript設計模式》Ross Harmes,Dustin Dial著,謝廷晟 譯,人民郵電出版社出版
(2)《適配器模式_百度百科》:
https://baike.baidu.com/view/3371585.htm
摘自 蜜果私塾