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); // 假设没有实现接口中的所有方法的话,将会抛出错误 ... }
推断实现某个接口非常easy。仅仅要某个类中的方法名与接口中一致。那么就觉得是实现了这个接口。就如同听到嘎嘎叫就是鸭子一般。
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中,还须要通过辅助类和方法来强制保证某个类实现了一个接口,因此用不用接口。还须要结合详细情况来推断。