接口:提供一种说明一个对象应该有哪些方法的手段
JavaScript中有三种方式实现接口:
(1)注释描述接口
(2)属性检测接口
(3)鸭式辨型接口
1、注释描述接口:不推荐
优点:易于实现,不需要额外的类或函数。
缺点:纯文档约束,程序不能检查实现接口的对象是否实现了所有接口方法
1 /** 2 * interface Composite{ 3 * function a(); 4 * function b(); 5 * } 6 */ 7 // CompositeImpl implements Composite 8 var CompositeImpl = function(){ 9 //业务逻辑 10 }; 11 CompositeImpl.prototype.a = function(){ 12 //业务逻辑 13 }; 14 CompositeImpl.prototype.b = function(){ 15 //业务逻辑 16 };
2、属性检测接口:不推荐
第二种方法要更严谨一点。所有类都明确地声明自己实现了哪些接口,那些想与这些类打交道的对象可以针对这些声明进行检查。那些接口自身仍然只是注释,但现在你可以通过检查一个属性得知某个类自称实现了什么接口。
优点:能够检查实现了哪些接口
缺点:并未确保类真正实现了自称实现的接口。你只知道它是否说自己实现了接口。
1 var interfacesImpl = function(){ 2 //在实现类内部用一个数组保存要实现的方法名 3 //通常这个属性名是团队中规定好的 4 //声明自己实现了这两个方法,但实际上并不一定 5 this.implementsInterfaces = ["Composite","FormItem"]; 6 }; 7 8 //专门为这个实现对象写一个检测函数,传入实例对象,用于检查实例对象是否实现了所有接口 9 function checkImplements(obj){ 10 //调用检查方法 obj是否实现了两个接口,如果没有都实现则抛出异常 11 if(!isImplements(obj,"Composite","FormItem")){ 12 throw new Error("接口没有全部实现!"); 13 } 14 //obj是要检查的对象 15 function isImplements(obj){ 16 //传入的第0个参数是要检查的对象,所以从1开始检查 17 for(var i=1; i<arguments.length; i++){ 18 //接收接口中每个接口的名字 19 var interfaceName = arguments[i]; 20 //默认未实现该接口 21 var foundFlag = false; 22 //循环查询传入实例对象的实现接口数组,检查是否全部实现 23 for(var j=0; j<obj.implementsInterfaces.length; j++){ 24 //如果实现了这个接口,就修改标记并跳出 25 //debugger 26 if(obj.implementsInterfaces[j] == interfaceName){ 27 foundFlag = true; 28 break; 29 } 30 } 31 //如果遍历实现接口数组之后没找到,返回false 32 if(!foundFlag){ 33 return false; 34 } 35 } 36 return true; 37 } 38 } 39 40 //使用实例对象并检测 41 var o = new interfacesImpl(); 42 checkImplements(o);
3、鸭式辨型法:推荐
背后的观点:如果对象具有与接口定义的方法同名的所有方法,那么久可以认为它实现了这个接口。
1 /** 2 * 接口类 3 * 4 * @param {String} name 接口的名字 5 * @param {Array} methods 要实现方法名称的数组 6 */ 7 var Interface = function (name, methods) { 8 //判断参数个数 9 if(arguments.length !== 2){ 10 throw new Error("接口构造器参数必须是两个!"); 11 } 12 this.name = name; 13 this.methods = []; 14 for(var i=0; i<methods.length; i++){ 15 if(typeof methods[i] !== "string"){ 16 throw new Error("接口实现的函数名称必须是字符串!"); 17 } 18 this.methods.push(methods[i]); 19 } 20 } 21 22 //实例化接口对象---传入接口名和要实现的方法数组 23 var CompositeInterface = new Interface("CompositeInterface",["add","remove"]); 24 var FormItemInterface = new Interface("FormItemInterface",["update","select"]); 25 26 //实现接口的类 27 var CompositeImpl = function(){ 28 29 } 30 31 //实现接口的方法 32 CompositeImpl.prototype.add = function(obj){ 33 //... 34 } 35 CompositeImpl.prototype.remove = function(obj){ 36 //... 37 } 38 CompositeImpl.prototype.select = function(obj){ 39 //... 40 } 41 //在这里少实现一个方法,下面检查是否全部实现了接口 42 // CompositeImpl.prototype.update = function(obj){ 43 // //... 44 // } 45 46 //实例化 实现接口的对象 47 var c = new CompositeImpl(); 48 49 //检验接口里的方法是否全部实现,如果不通过则抛出异常 50 Interface.ensureImplements = function(obj){ 51 //如果接收到参数小于2,说明异常 52 if(arguments.length < 2){ 53 throw new Error("接口检查方法的参数必须多余两个!"); 54 } 55 //接口实现检查 56 for(var i=0,len = arguments.length; i<len; i++){ 57 //获取当前接口 58 var instanceInterface = arguments[i]; 59 //判断接收到的是不是接口的对象,如果不是则抛出异常 60 if(instanceInterface.constructor !== Interface){ 61 throw new Error("接口检测函数必须传入接口对象!"); 62 } 63 //检查实例化接口的对象是不是实现了接口里的所有方法 64 for(var j=0; j<instanceInterface.methods.length; j++){ 65 //接收到的字符串方法 66 var methodName = instanceInterface.methods[j]; 67 //如果obj里面没有methodsName这个方法,或者有这个属性但是不是函数,就抛出异常 68 if(!obj[methodName] || typeof obj[methodName] !== "function"){ 69 throw new Error("接口方法" + methodName + "没有实现!"); 70 } 71 } 72 } 73 } 74 75 //传入要检查的类,和要实现的所有接口对象 76 Interface.ensureImplements(c, CompositeInterface, FormItemInterface); 77 c.add();