1、定義javascript
適配器模式可用來在現有接口和不兼容的類之間進行匹配。使用這種模式的對象又叫包裝器(wrapper),由於它們是在用一個新的接口包裝另外一個對象。在設計類的時候旺旺會遇到有些接口不能與現有API一同使用的狀況。藉助於適配器,你不用直接修改這些類也能使用它們。 html
適配器能夠被添加到現有代碼中以協調兩個不一樣的接口。若是現有代碼的接口能很好的知足須要,那就可能沒有必要使用適配器。但要是現有接口對於手頭的工做來講不夠直觀或實用,那麼可使用適配器來提供一個更簡化或更豐富的接口。java
var clientObject = { string1: 'foo', string2: 'bar', string3: 'baz' }; function interfaceMethod(str1, str2, str3) { // ... } // 爲了把clientObject做爲參數傳遞給interfaceMethod,須要用到適配器。 function clientToInterfaceAdapter(o) { interfaceMethod(o.string1, o.string2, o.string3) } // 如今就能夠把整個對象傳遞給這個函數 clientToInterfaceAdapter(clientObject);
clientToInterfaceAdapter函數的做用就在於對interfaceMethod函數進行包裝,並把傳遞給它的參數轉換爲後者須要的形式。設計模式
在這裏,嵌入進大叔的鴨子與火雞的例子。app
鴨子(Dock)有飛(fly)和嘎嘎叫(quack)的行爲,而火雞雖然也有飛(fly)的行爲,可是其叫聲是咯咯的(gobble)。若是你非要火雞也要實現嘎嘎叫(quack)這個動做,那咱們能夠複用鴨子的quack方法,可是具體的叫還應該是咯咯的,此時,咱們就能夠建立一個火雞的適配器,以便讓火雞也支持quack方法,其內部仍是要調用gobble。框架
//鴨子 var Duck = function(){ }; Duck.prototype.fly = function(){ throw new Error("該方法必須被重寫!"); }; Duck.prototype.quack = function(){ throw new Error("該方法必須被重寫!"); }//火雞 var Turkey = function(){ }; Turkey.prototype.fly = function(){ throw new Error(" 該方法必須被重寫 !"); }; // 火雞——咯咯地叫 Turkey.prototype.gobble = function(){ throw new Error(" 該方法必須被重寫 !"); };//具體的鴨子 var MallardDuck = function () { Duck.apply(this); }; MallardDuck.prototype = new Duck(); //原型是Duck MallardDuck.prototype.fly = function () { console.log("能夠飛翔很長的距離!"); }; // 鴨子——嘎嘎叫 MallardDuck.prototype.quack = function () { console.log("嘎嘎!嘎嘎!"); };// 具體的火雞 var WildTurkey = function () { Turkey.apply(this); }; WildTurkey.prototype = new Turkey(); //原型是Turkey WildTurkey.prototype.fly = function () { console.log("飛翔的距離貌似有點短!"); }; WildTurkey.prototype.gobble = function () { console.log("咯咯!咯咯!"); };// 讓火雞也支持quack方法的適配器 var TurkeyAdapter = function(oTurkey) { Duck.apply(this); this.oTurkey = oTurkey; }; TurkeyAdapter.prototype = new Duck(); TurkeyAdapter.prototype.fly = function() { // 調用火雞的飛方法 this.oTurkey.fly(); }; // 讓火雞也有鴨子的方法 TurkeyAdapter.prototype.quack = function() { this.oTurkey.gobble(); }// 調用 var oMallardDuck = new MallardDuck(); oMallardDuck.fly(); // 能夠飛翔很長的距離! oMallardDuck.quack(); // 咯咯!咯咯! var oWildTurkey = new WildTurkey(); oWildTurkey.fly(); // 飛翔的距離貌似有點短! oWildTurkey.gobble(); // 咯咯!咯咯! var oTurkeyAdapter = new TurkeyAdapter(oWildTurkey); // 調用原有的火雞的飛方法 oTurkeyAdapter.fly(); // 飛翔的距離貌似有點短! // 調用鴨子特有的嘎嘎叫的方法 - 從而達到適配的目的 oTurkeyAdapter.quack(); // 咯咯!咯咯!這樣,有了TurkeyAdapter適配器,使得具體的實例對象oWildTurkey一樣具備了Duck具備的方法quack。函數
2、適用場景工具
適配器適用於客戶系統期待的接口與現有API提供的接口不兼容這種場合。它只能用來協調語法上的差別問題。適配器所適配的兩個方法執行的應該是相似的任務,不然的話它就解決不了問題。若是客戶想要的是一個不一樣的接口,好比說一個他們用起來更容易一些的接口,那麼也能夠爲此設計適配器。就像橋接元素和門面元素同樣,經過建立適配器,能夠把抽象與其實現隔離開來,以便二者獨立變化。
this
在這裏,引入進大叔博客深刻理解JavaScript系列(39):設計模式之適配器模式中適用場景的歸納——spa
1.使用一個已經存在的對象,但其方法或屬性接口不符合你的要求;
2.你想建立一個可複用的對象,該對象能夠與其它不相關的對象或不可見對象(即接口方法或屬性不兼容的對象)協同工做;
3.想使用已經存在的對象,可是不能對每個都進行原型繼承以匹配它的接口。對象適配器能夠適配它的父對象接口方法或屬性。
3、優點
適配器有助於避免大規模改寫現有客戶代碼。其工做機制是:用一個新的接口獨享對現有類的接口進行包裝,這樣客戶程序就能使用這個並不是爲其量身打造的類而又勿需爲此大動手腳。
4、劣勢
1.可能有些工程師不想使用適配器,其緣由主要在於他們實際上須要完全重寫代碼。有人認爲適配器是一種沒必要要的開銷,徹底能夠經過重寫現有代碼避免。
2.適配器模式也會引入一批須要支持的新工具
3.若是現有API還未定形,或者新接口還未定型(這更有可能),那麼適配器可能不會一直管用。
5、與其餘模式的區別
1.與門面模式的區別:從表面上看,適配器模式很像門面模式。它們都要對別的對象進行包裝並改變其呈現的接口。兩者的差異在於如何改變接口。門面元素展示的是一個簡化的接口,它並不提供額外的選擇,並且有時爲了方便完成常見任務它還會作出一些假設。而適配器則要把一個接口轉換爲另外一個接口,它並不會濾除某些能力,也不會簡化接口。若是客戶端系統期待的接口不可用,那就須要用到適配器。
2.與橋接模式的區別:適配器和橋接模式雖然相似,但橋接的出發點不一樣,橋接的目的是將接口部分和實現部分分離,從而對他們能夠更爲容易也相對獨立的加以改變。而適配器則意味着改變一個已有對象的接口。
3.與裝飾者模式的區別:裝飾者模式加強了其它對象的功能而同時又不改變它的接口,所以它對應程序的透明性比適配器要好,其結果是裝飾者支持遞歸組合,而純粹使用適配器則是不可能的。
4.與代理模式的區別:代理模式在不改變它的接口的條件下,爲另一個對象定義了一個代理。
6、小結
適配器模式是一種頗有用的技術,它能夠用來對類和對象進行包裝,以便向客戶代碼提供其期待的接口。應用這種技術,你能夠在不影響現有實現的前提下利用新的更好的接口。做爲一個實現者,你能夠根據本身的須要定製接口。這種模式的確會引入一些新代碼,不過,在涉及大型系統和遺留框架的狀況下,它的優勢每每比缺點更突出。
源自:JavaScript設計模式(人民郵電出版社)——第十一章,適配器模式