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设计模式(人民邮电出版社)——第十一章,适配器模式