接口:提供一種說明一個對象應該有哪些方法的手段
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();