javascript设计模式开篇:Javascript 接口的实现
javascript语言不像java、 c#、 c++等面向对象语言那样有完备的接口支持,在javascript中,接口的实现有三种方式,分别为注释描述、属性检查、鸭式变形。注释描述实现起来最为简单,但是,接口约定的遵守纯靠自觉,而且也没有很好的度量措施,说到底,它主要还是属于程序文档范畴。其实,类是否申明自己支持哪些接口并不重要,只要它具有这些接口中的方法就行了。鸭式变形(这个名称来自James Whitcomb Riley的名言:"像鸭子一样嘎嘎叫的就是鸭子")正式基于这种认识。
下面来谈谈javascript中接口的具体实现策略。(基于鸭式变形,用的也是最多的哦~~)
* 接口类构造函数,接受2个以上的参数,其中第一个参数为接口名,后面的参数可以为字符串数组,也可以为字符串 * @param {Object} name * 接口名 * @param {Object} methods * 接口包含的方法集合,参数可以为数组,也可以传入任意多的字符串形式的方法名 */ var Interface = function(name, methods){ if(arguments.length < 2){ //若参数个数不为2,则抛出错误 throw new Error("Interface constructor called with" + arguments.length + "arguments, but expected at least 2"); } this.name = name; this.methods = []; for(var i = 1, len = arguments.length; i < len; ++i){ if(arguments[i] instanceof Array){ //若参数为数组,则遍历该参数 for(var j = arguments[i].length - 1; j > -1; --j){ if(typeof arguments[i][j] !== 'string' ){//保证传入的方法名为字符串,否则抛出错误 throw new Error('Interface constructor expects method names to be passed in as a string'); } this.methods.push(arguments[i][j]); //保存方法名 } } else if(typeof arguments[i] === 'string'){ //参数为字符串,直接保存 this.methods.push(arguments[i]); } else { //否则抛出错误 throw new Error('Interface constructor expects method names to be passed in as a string'); } } }; /* * 接口实现检验函数,第一个参数为要检查的对象,后面的任意参数为实现的接口对象,也可以为接口对象数组 * @param {Object} object */ Interface.ensureImplents = function(object){ if(arguments.length < 2){ throw new Error("Interface constructor called with" + arguments.length + "arguments, but expected at least 2"); } var _checkMethods = function(inface){ //内部函数,用于检验对象是否实现了ifs对象中的方法 var methods = inface.methods, i = methods.length - 1; for( ; i > -1; --i){ var method = methods[i]; //若对象不存在该属性,或者该属性不是方法,那么抛出错误 if(typeof object[method] === 'undefined' || typeof object[method] !== 'function'){ throw new Error("Function Interface.ensureImplents: object does not implent the " + inface.name + "interface. Method " + method + " was not found." ); } } }; for (var i = arguments.length - 1; i > 0; --i) { if(arguments[i] instanceof Array){ for(var j = arguments[i].length - 1; j > -1; --j){ if(!arguments[i][j] instanceof Interface){ throw new Error('Function Interface.ensureImplents expects arguments two and above to be' + 'instances of Interface'); } _checkMethods(arguments[i][j]); //检验接口实现 } } else if(arguments[i] instanceof Interface){ _checkMethods(arguments[i]); //检验接口实现 } else { throw new Error('Function Interface.ensureImplents expects arguments two and above to be' + 'instances of Interface'); } } };
编写出上述代码挺简单,主要思路就是检测传入参数的类型和名称。看看某个具体实现类有没有实现所定义的接口中的那些function。。
下面给出一个调用接口的例子吧~
//2个接口实例 var ITest = new Interface('ITest', 'test0', ['test1', 'test2'], 'test3'); var ISay = new Interface('ISay', 'say0', 'say1'); //未继承的任何对象,仅是实现了ITest接口 var implentTest = { test0: function(){ alert('test0'); }, test1:function(){ alert('test1'); }, test2:function(){ alert('test2'); }, test3:function(){ alert('test3'); } } var implentSay = { say1:function(){ alert('say1'); }, say0:function(){ alert('say0'); } }; function TestAndSay (){ this.say0 = function(){ alert('test0 and say0'); }; this.say1 = function(){ alert('tes1t and say1'); }; } TestAndSay.prototype = implentTest; //一个简单的继承 var implentTestAndSay = new TestAndSay(); //实例化 function test(ITestInstance){ Interface.ensureImplents(ITestInstance, ITest); ITestInstance.test0(); ITestInstance.test1(); ITestInstance.test2(); } function say(ISayInstance){ Interface.ensureImplents(ISayInstance, ISay); ISayInstance.say0(); ISayInstance.say1(); } function sayAndtest(inst){ Interface.ensureImplents(inst, ISay, ITest); inst.test0(); inst.say0(); inst.say1(); } test(implentTest);//弹出3个对话框:test0 test1 test2 //test(implentSay);将会抛出错误 //say(implentTest); 将会抛出错误 say(implentSay);//弹出2个对话框 say0 say1 sayAndtest(implentTestAndSay); //弹出3个对话框: test0; test0 and say0; test1 and say1
哈哈,有了接口的定义及实现后,你就可以在设计模式中充分发挥你的才能了,让设计模式为我们服务吧!