接口是提供一种以说明一个对象应该有哪些方法的手段。接口是面向对象javascript程序员的工具箱中最有用的工具之一,那么对于javascript没有定义接口的概念,我们通过模拟高级程序语言的方式来创建javascript中的接口。
通常建立javascript接口有三种方式:
注释描述接口
/**
* interface Composite {
* function add(obj);
* function remove(obj);
* function uopdate(obj);
* }
*/
// CompositeImpl implements Composite
var CompositeImpl = function(){
};
CompositeImpl.prototype.add = function(obj){
// do something ...
}
CompositeImpl.prototype.remove = function(obj){
// do something ...
}
CompositeImpl.prototype.update = function(obj){
// do something ...
}
var c1 = new CompositeImpl();
var c2 = new CompositeImpl();
alert(c1.add == c2.add); //true
注解描述的方式,优点是程序员可以有一个参考,缺点是这种还是属于文档的范畴,这种方式太松散,没有检查接口的方法是否完全被实现。
如果这些方法都定义在函数内,那么每次新建对象都会重新创建函数内的方法,对性能会有很大影响,所以通过原型的方式能够很好解决这个问题
属性检测方法
/**
* interface Composite {
* function add(obj);
* function remove(obj);
* function uopdate(obj);
* }
*
* interface FormItem {
* function select(obj);
* }
*
*/
// CompositeImpl implements Composite , FormItem
var CompositeImpl = function(){
// 显示地在类的内部,定义接受所实现的接口
// 一般来说是一个规范 在类的内部定义一个数组(名字要固定,例如implementsInterfaces )
this.implementsInterfaces = ['Composite' ,'FormItem' ];
};
CompositeImpl.prototype.add = function(obj){
// do something ...
alert('add...');
};
CompositeImpl.prototype.remove = function(obj){
// do something ...
};
CompositeImpl.prototype.update = function(obj){
// do something ...
};
CompositeImpl.prototype.select = function(obj){
// do something ...
};
// 检测CompositeImpl类的对象的
function CheckCompositeImpl(instance){
//判断当前对象是否实现了所有的接口
if(!IsImplements(instance,'Composite','FormItem')){
throw new Error('Object does not implement a required interface!');
}
}
// 公用的具体的检测方法(核心方法) 返回值类型 boolean
// 这个方法的主要目地:就是判断 实例对象 有没有实现相关的接口
//之所以抽离出来是为了解耦,当判断其他对象时,改方法可以重用,只用定义多个类似上面CheckCompositeImpl的函数
function IsImplements(object){
// arguments 对象 获得函数的实际参数
//object就是传入的第一个对象实例参数,下面的循环只用从1开始获取接口列表
for(var i = 1 ; i < arguments.length;i++){
//接受所实现的每一个接口的名字
var interfaceName = arguments[i];
//判断此方法到底成功 还是失败啊
var interfaceFound = false ;
for(var j = 0 ; j <object.implementsInterfaces.length;j++){
if(object.implementsInterfaces[j] == interfaceName) {
interfaceFound = true ;
break;
}
}
if(!interfaceFound){
return false ;
}
}
return true ;
}
var c1 = new CompositeImpl();
CheckCompositeImpl(c1);
c1.add();
这种方式也只能够够判断是否实现了接口,但是无法具体到判断是否实现了接口的某些方法
鸭式辨型法
//鸭式辨型法实现接口(最完美的javascript实现接口方式)
//之前所说的注解描述法、属性检测法都有一定的缺陷
//鸭式辨型法的核心是一个类实现了接口里的所有方法,完全面向对象,代码也实现统一和解耦
//一:接口类Class Interface实现了多个接口
/**
* 接口类需要2个参数
* 参数1: 接口的名字 (string)
* 参数2: 接收方法名称的集合(数组) (array)
*/
var Interface = function(name, methods) {
//判断接口的参数格式
if (arguments.length != 2) {
throw new Error('this instance interface constructor arguments must be 2 length!');
}
this.name = name;
this.methods = [];
for (var i = 0, len = methods.length; i < len; i++) {
if (typeof methods[i] !== 'string') {
throw new Error('the Interface method name is error!');
}
this.methods.push(methods[i]);
}
};
//二:准备工作
//1 实例化接口对象
var CompositeInterface = new Interface('CompositeInterface', ['add', 'remove']);
var FormItemInterface = new Interface('FormItemInterface', ['update', 'select']);
// CompositeImpl implements CompositeInterface , FormItemInterface
// 2 具体的实现类
var CompositeImpl = function() {
};
// 3 实现接口的方法implements methods
CompositeImpl.prototype.add = function(obj) {
alert('add');
// do something ...
};
CompositeImpl.prototype.remove = function(obj) {
alert('remove');
// do something ...
};
CompositeImpl.prototype.update = function(obj) {
alert('update');
// do something ...
};
CompositeImpl.prototype.select = function(obj) {
alert('select');
// do something ...
};
// 三:检验接口里的方法
// 如果检验通过 不做任何操作 不通过:浏览器抛出error
// (c1, CompositeInterface, FormItemInterface)只关注第一个参数,其他用arguments来获得
Interface.ensureImplements = function(object) {
if (arguments.length < 2) {
throw new Error('Interface.ensureImplements method constructor arguments must be >= 2!');
}
//获得接口实例对象
for (var i = 1, len = arguments.length; i < len; i++) {
//判断参数是否是接口类型
var instanceInterface = arguments[i];
if (instanceInterface.constructor !== Interface) {
throw new Error('the arguments constructor not be Interface Class');
}
//循环接口实例对象里面的每一个方法
for (var j = 0; j < instanceInterface.methods.length; j++) {
//用一个临时变量接收每一个方法的名字
var methodName = instanceInterface.methods[j];
if (!object[methodName] || typeof object[methodName] != 'function') {
throw new Error("the method name '" + methodName + "' is not found !");
}
}
}
};
var c1 = new CompositeImpl();
Interface.ensureImplements(c1, CompositeInterface, FormItemInterface);
c1.add();
接口的利弊
- 对于一些中小型程序来说,使用接口很显然是不明智的,对项目来说接口的好处也不明显,只是徒增其复杂度而已
- 对于接口的好处,那么显而易见,首先是促进代码的重用,对于开发来讲,还可以告诉程序员哪些类都使用了什么方法,如果你事先知道接口那么就减少了再编码的时候对类与类之间的冲突,实现解耦。对于测试和调试也会变得轻松,用于javascript的弱类型语言,类型不匹配经常出现,那么使用接口,这一点会变得容易一些。