JavaScript设计模式: 接口模仿
JavaScript中模仿接口有三种方法
1. 注释法
/* interface Composite(){ function add(child); function remove(child); function getChild(index); } interface FormItem(){ function save(); } */
缺点:无法确保真正实现接口中的方法集,也不会抛出错误以告知用户程序中有问题,对测试和调试没有什么帮助。它主要属于程序文档范畴,在这种做法中,对接口约定的遵守完全依靠自觉。
优点:不需要额外的类或函数,不影响文件尺寸或执行速度,因为所用的注释可以在对代码进行部署时予以剔除
2. 属性检查
类通过增加一个implementsInterfaces的数组属性来显式自己支持什么接口,任何一个要求其参数属于特定类型的函数都可以对这个属性进行检查,并在所需接口未在声明这列时抛出一个错误。
var CompositeForm=function(id, method, action){ this.implementsInterfaces=['Composite','FormItem']; } function addForm(formInstance){ if(!implements(formInstance, 'Composite', 'FormItem')){ throw new Error('Object does not implements a required interface'); } } function implements(object){ for(var i=1;i<arguments.length;i++){ var interfaceName=arguments[i]; var interfaceFound=false; for(var j=0;j<object.implementsInterfaces.length;j++){ if(object.implementsInterfaces[j]==interfaceName){ interfaceFound=true; break; } } if(!interfaceFound){ return false; } } return true; }
优点:对类所实现的接口提供了文档说明。如果需要的接口不在一个类所声明的接口列表中,你会看到错误消息。
缺点:不能确保真正实现了自称实现的接口。你只知道它是否说自己实现了接口。在创建一个类时声明它实现了一个接口,但后来在实现该接口所规定的方法时却漏掉其中的某一个,这种错误很常见。此时所有检查都能通过,但所需的方法却不存在,这将在代码中埋下隐患。
3. 鸭辨型法
"像鸭子一样走路并且嘎嘎叫的就是鸭子"。类是否声明自己支持哪些接口并和重要,只要它具有这些接口中的方法就行。如果对象具有与接口定义的方法同名的所有方法,那么就可以认为它实现了这个接口。
var Composite=new Interface('Composite', ['add','remove','getChild']); var FormItem=new Interface('FormItem', ['save']); var CompositeForm=function(id, method, action){ } function addForm(formInstance){ Interface.ensureImplements(formInstance, Composite, FormItem); } var Interface=function(name, methods){ if(arguments.length!=2){ throw new Error("Interface constructor called with "+arguments.length+ " arguments, but expected exactly 2.") } this.name=name; this.methods=[]; for(var i=0, len=methods.length;i<len;i++){ if(typeof methods[i]!=='string'){ throw new Error('Interface constructor expects method names to be passed in as a string'); } this.methods.push(methods[i]); } } Interface.ensureImplements=function(object){ if(arguments.length<2){ throw new Error('Function Interface.ensureImplements called with '+arguments.length+' arguments, but expected at least 2.'); } for(var i=1, len=arguments.length;i<len;i++){ var interface=arguments[i]; if(arguments.constructor!=Interface){ throw new Error('Function Interface.ensureImplements expected arguments two and above to be instances of Interface.'); } for(var j=0, methodsLen=interface.methods.length;j<methodsLen;j++){ var method=interface.methods[j]; if(!object[method] || typeof object[method]!=='function'){ throw new Error('Function Interface.ensureImplements: object does not implement the '+interface.name+' interface.Method '+method+' was not found.'); } } } }
缺点:类并不声明自己实现了哪些接口,这降低了代码的可重用性,并且也缺乏其他两种方法那样的自我描述性。它需要使用一个辅助类(Interface)和一个辅助函数(ensureImplements),它只关心方法的名称,并不检查参数的名称、数目或类型。