1.什么是接口
学习过《设计模式》的亲们可能听说过编程的一条原则:“针对接口编程而不是实现编程”。那么到底什么是接口呢?
接口定义了一个对象应该有的那些方法的手段,但是不具体规定这些方法的具体实现。学习过C#,或者VB.NET的可能会很清楚。所谓的接口就是一个只有方法名,但没有实现的某种特殊的类。
VB.NET中的接口
Public Interface Person Sub say() Sub eat() End Interface Public Class Man Implements Person Public Sub eat() Implements Person.eat End Sub Public Sub say() Implements Person.say End Sub End Class
注意:在VB.NET中,如果实现类没有实现接口中全部方法时,编译器会报错。
问题在于,JavaScript中没有内置的创建或实现接口的方法,它也没有内置的方法可以用于判断一个对象是否实现了接口的一套方法。但是通过一定的模仿,就可是实现所谓的接口。
2.JavaScript中模仿接口
1)用注释描述接口
/* 一个关于人类的接口 interface Person { function say(); function eat(); } */ //在上述注释中只是简单的定义了接口的名字和方法,然后通过看注释直接实现接口即可 var Man = function(id, method, action) { // 实现了Person这个接口 ... }; // 实现接口中的方法 Man.prototype.say = function() { ... }; Man.prototype.eat = function() { ... };
最简单的一种写法,由于接口没有具体实现,只要知道要实现的功能即可,所以把要实现的接口写到注释里面,让实现的类知道实现的方法即可。
2)用属性检查模仿接口
相比用注释描述接口来说,这种方法略微严密一点,可以声明自己实现了那些接口,想与这些类打交道的对象可以针对声明进行检查。在此,接口仍是注释,但是多了一个检查属性的方法,通过这个方法可以知道某个类实现了那个接口。
3)鸭式辨型模仿接口
// Interfaces. //定义一个接口 var Composite = new Interface('Composite', ['add', 'remove', 'getChild']); var FormItem = new Interface('FormItem', ['save']); // 定义一个类 var CompositeForm = function(id, method, action) { ... }; ... function addForm(formInstance) { ensureImplements(formInstance, Composite, FormItem); // 如果没有实现接口中的全部方法的话,将会抛出错误 ... }
判断实现某个接口很简单,只要某个类中的方法名与接口中一致,那么就认为是实现了这个接口。就如同听到嘎嘎叫就是鸭子一般。
3.模仿接口的过程
1)学习过其他语言的亲们,我们知道所谓的接口就是包含了一些没有实现的方法的集合。因此如果要模仿接口的话,在JavaScript中,我们必须给Interface类增加一个数组的成员:methods,用来保存我们的这些方法名。另外还需要一个参数:name。用来方便以后我们检测,实现类到底实现了哪一个接口。
2)正如上述C#编写的接口所示,在C#中若一个类继承了某个接口,但是并没有全部实现其方法的时候,编译器会报错,从而提醒我们。但是JavaScript并没有这个功能。所以我们需要来定义一个通用的方法:ensureImplements。来提醒我们,实现类是否实现了接口的全部方法。
4.接口的实现代码
<span style="font-family:SimSun;font-size:18px;">/* * 接口类构造函数,接受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'); } } }; </span>
5.牛刀小试
<script src="JAVASCRIPT/jquery-1.3.1.js"></script> <script src="JAVASCRIPT/interface.js"></script> <script> //定义一个人的接口,拥有两个方法say和eat var IPerson = new Interface('IPerson', 'say', 'eat'); //定义实现接口的类 var Man = { say: function () { alert("我会说话"); }, eat: function () { alert("我也能吃饭") } } //定义一个验证是否实现的方法 function test(ITestInstance) { //调用接口中的验证方法,来验证是否实习类实现了全部的方法 Interface.ensureImplents(ITestInstance, IPerson); ITestInstance.say(); ITestInstance.eat(); } test(Man); //弹出两个对话框 </script><span style="font-size:18px;color:#c0c0c0;"> </span>
6.总结
接口为我们带来便利的同时也会带来很多的难处,尤其在JavaScript中,还需要通过辅助类和方法来强制保证某个类实现了一个接口,因此用不用接口,还需要结合具体情况来判断。
6.总结
接口为我们带来便利的同时也会带来很多的难处,尤其在JavaScript中,还需要通过辅助类和方法来强制保证某个类实现了一个接口,因此用不用接口,还需要结合具体情况来判断。
原文:http://blog.csdn.net/luckyzhoustar/article/details/38778965