01.Javascript中的接口----Interface [转载]

转载地址:http://blog.csdn.net/ymmc001/archive/2010/11/05/5990021.aspx

本文主要讲述如何用原生的Javascript代码来模拟并实现接口

前言

众所周知,在Java、C#等语言中,接口由专门的关键字interface来定义,而接口的实现则有implements关键字来完成,接口有什么特点呢?简单地说有(不完全归纳):

  1. 不可被实例化
  2. 所有方法都是抽象方法
  3. 所有属性都是public static final的

但是在Javascript语言中,没有现成的关键字可以直接用来定义一个接口,也没有现成的关键字可以直接用来实现所谓的extends和implements。既然没有现成的,下面将阐述如何用原生的Javascript代码来模拟并实现接口。

Javascript中的接口

接口的定义

 /**
     * Interface构造器
     * @param {String} name 接口名
     * @param {Array} methods 接口中的抽象方法     
     * @exception {Error} 参数不合格时抛出异常
     * @example
     *      var IActionListener = new Interface("IActionListener",["method1","method2"]);
     */
    var Interface = function(name, methods) {
        if(arguments.length != 2) {
            throw new Error("Interface有且只有两个参数,而当前参数个数为:" + arguments.length );
        }
        this.name = name;           
        this.methods = [];          
        for(var i = 0, len = methods.length; i < len; i++) {                
            if(typeof methods[i] !== 'string') {                    
                throw new Error("Interface中的方法名必须合法");             
            }
           this.methods.push(methods[i]);          
        }       
    };

这个Interface类是用来模仿Java中的interface关键字的,但不同的是,Java中通过interface IActionListener{}的形式来定义一个接口,而这里则是通过var IActionListener = new Interface("IActionListener",["method1"])的形式来定义。

这个接口类接受两个参数,第一个表示需要定义的接口名称,第二个参数表示该接口中即将声明的抽象方法。该类在产生新接口的时候,会检查参数的合法性,只要通过检查,接口便产生了,同时具有两个属性:接口名和方法列表。

接口的实现

 /**
     * 接口实现的检查,保证object是InterfaceN的实例
     * @param {Object} oInterface 待检查的接口实例
     * @param {Class} InterfaceN 被实现的Interface
     * @exception {Error} 参数不合格时抛出异常
     * @example 
     *      Interface.ensureImplements(oActionListener,IActionListener);
     */
    Interface.ensureImplements = function(oInterface,Interface1,Interface2,InterfaceN) {
        if(arguments.length < 2) {
            throw new Error("Interface.ensureImplements方法至少需要两个参数,而当前参数个数为:" + arguments.length);
        }
        for(var i = 1, len = arguments.length; i < len; i++) {
            var interface = arguments[i];
            if(interface.constructor !== Interface) {
                throw new Error(interface + "不是Interface的实例,不是接口");
            }
            for(var j = 0, methodsLen = interface.methods.length; j < methodsLen; j++) {
                var method = interface.methods[j];
                if(!oInterface[method] || typeof oInterface[method] !== 'function') {
                    throw new Error("所给参数没有实现" + interface.name + "接口," + method + "未找到");
                }
            }
        }
    };

这个静态方法是用来模仿Java中的implements关键字的,但不同的是,Java中通过class ActionListener implements IActionListener{}来实现的,而这里则是通过interface.ensureImplements(oActionListener,IActionListener)的形式来保证的。

这个静态方法接受两个参数,第一个表示接口实现类的实例对象,第二个参数表示接口实现类(当然了,这里其实是一个对象)。方法的内部会校验实例对象是否都实现了接口中的所有抽象方法,如果在该实例中存在没有被实现的抽象方法(方法未找到),则表明该实例对象不是接口实现类的实例对象。

接口实例的创建与使用

//创建接口
    var IActionListener = new Interface("IActionListener",["method1","method2"]);        
    //接口实例的创建
    var oActionListener = {
        method1 : function(){
            alert("这是方法1");
        },
        method2 : function(){
            alert("这是方法2");
        }
    };
    //implements确认
    Interface.ensureImplements(oActionListener,IActionListener);
    //调用实例中的方法
    oActionListener.method1();

这是一个简单的接口定义并实现的过程。

在Javascript中,必然存在着很多种接口的模拟方式,这里只是其中的一种实现思路。

模拟之不当,还请见谅。

非常希望您能提出宝贵的意见。

从ensureImplements方法到implements方法的升级(2010-09-22更新)

前面定义的ensureImplements(oInterface,Interface1,Interface2,InterfaceN)是通过实例来保证的,但是按照常理来说,一个接口被实现以后,便可以进行实例化,但是这里不是真正意义上的实现,而是一种检查、一种保证。为了让接口实现后仍然能通过new关键字实例化对象,下面需要将原来的方法进行升级,于是我定义了下面的方法:implements(ImplementsClass,Interface1,Interface2,InterfaceN),代码如下:

/**
 * 接口的实现
 * @param {function} ImplementsClass 待实现的类
 * @param {object} InterfaceN 被实现的Interface,Interface的实例
 * @exception {Error} 参数不合格时抛出异常
 * @example 
 *      implements(ActionListener,IActionListener);
 */
 var implements = function(ImplementsClass,Interface1,Interface2,InterfaceN){
    if(arguments.length < 2) {
        throw new Error("Interface.ensureImplements方法至少需要两个参数,而当前参数个数为:" + arguments.length);
    }
    //保证ImplementsClass的类型为function
    if(typeof arguments[0] !== "function"){
        throw new Error("实现类的类型必须为function");
    }
    for(var i = 1, len = arguments.length; i < len; i++) {
        var interface = arguments[i];
        if(interface.constructor !== Interface) {
            throw new Error(interface + "不是Interface的实例,不是接口");
        }
        //这里循环进行接口抽象方法的实现
        for(var j = 0, methodsLen = interface.methods.length; j < methodsLen; j++) {
            var method = interface.methods[j];
            if(!arguments[0].prototype[method]){
                arguments[0].prototype[method] = function(){};
            }
        }
    }
 }

经过这样的升级,我们的代码就可以写成这样了:

//创建接口
var IActionListener = new Interface("IActionListener",["method1","method2"]); 
//创建实现类
var ActionListener = function(){};
//实现
implements(ActionListener,IActionListener);
//这个时候,ActionListener.prototype已经是如下这个样子了:
/*
    ActionListener.prototype = {
        method1 : function(){},
        method2 : function(){}
    };
*/
//接下来可以真正的填充被空实现的方法的逻辑了
ActionListener.prototype = {
    method1 : function(){
        alert("这是方法1");
    },
    method2 : function(){
        alert("这是方法2");
    }
};
//调用实例中的方法
oActionListener.method1();

OK,一切正常。到此为止,在后续的文章中提到的implements方法,就是这个方法了,请务必留意,谢谢。