JavaScript实现接口的三种经典方式

tip:看此片文章之前建议先了解JavaScript原型

注释描述接口

注释描述接口: 不推荐

  • 优点:利用注解,给出参考
  • 缺点:纯文档约束,是一个假接口,程序不能检查实现接口对象是否实现所有接口方法
    /**
    * interface Composite{
    * function a();
    * function b();
    * }
    **/
    var CompositeImpl = function(){
    //业务逻辑
    }
    CompositeImpl.prototype.a = function(){
    //业务逻辑
    };
    CompositeImpl.prototype.b = function(){
    //业务逻辑
    };

属性检测接口

  • 优点:能够检测实现哪些接口
  • 缺点:没有完全脱离文档,不能检测是否实现每个接口里的所有方法
    interface Composite{
    function a();
    }
    interface FormItem(){
    function b();
    }
    // CompositeImpl implements Composite,FormItem
    var interfacesImpl = function(){
    //在实现类内部用一个数组保存要实现的方法名
    //通常这个属性名是团队中规定好的
    this.implementsInterfaces = ["Composite","FormItem"];
    };
    CompositeImpl.prototype.a = function(){
    //业务逻辑
    };
    CompositeImpl.prototype.b = function(){
    //业务逻辑
    };
    //专门为这个实现对象写一个检测函数,传入实例对象,用于检查实力对象是否实现了所有接口
    function checkImplements(obj){
    //调用检查方法 obj是否实现两个接口,如果没有都实现则抛出异常
    if(!isImplements(obj,"Composite","FormItem")){
    throw new Error("接口没有全部实现!");
    }
    //接收一个参数obj是要检查的对象
    function isImplements(obj){
    //arguments对象能够获取实际传入函数的所有参数的数组
    //传入的第0个参数是要检查的对象,所以从1开始检查
    for(var i = 1; i < arguments.length ; i++){
    //接收接口中每个接口的名字
    var interfaceName = arguments[i];
    //一个标记,是否实现这个接口,默认没有
    var foundFlag = false;
    //循环查询传入实例对象的实现接口数组 以检查是否全部实现
    for(var j = 0 ;j <obj.implementsInterfaces.length;j++){
    //如果 实现了这个接口 就修改标记跳出循环
    if(obj.implementsInterfaces[j]==interfaceName){
    foundFlag = true;
    break;
    }
    }
    //如果遍历实现接口数组之后没找到 就返回false
    if(!foundFlag){
    return false;
    }
    }
    //如果都找到了 返回true
    return true;
    }
    }
    //使用实力对象并检测
    var o = new interfacesImpl();
    checkImplements(o); //不会抛出异常 因为正确实现了两个接口
    //如果在写interfacesImpl内的implementsInterfaces列表的时候少写了,那么就会在检查函数中抛出异常

鸭式辨型法(目前开发中使用的方式)

实现接口实际上是检测对象是否拥有接口定义的所有方法。接口只是用来定义某些方法名称,它并不需要实现这些方法

  1. 接口类 Class Interface

    /**
    * 接口类需要的参数:
    * 1 接口的名字
    * 2 要实现方法名称的数组
    */
    var Interface = function( name , methods ){
    //判断参数个数
    if(arguments.length!=2){
    throw new Error("接口构造器参数必须是两个!");
    }
    this.name = name;
    this.methods = [];
    for(var i = 0;i<methods.length;i++){
    if( typeof methods[i] !== "string" ){
    throw new Error("接口实现的函数名称必须是字符串!");
    }
    this.methods.push(methods[i]);
    }
    };
  2. 准备工作:

    // 2.1 实例化接口对象    传入接口名 和 要实现的方法数组
    var CompositeInterface = new Interface("CompositeInterface",["add","remove"]);
    var FormItemInterface = new Interface("FormItemInterface",["update","select"]);
    // 2.2 实现接口的类
    //CompositeImpl implementes CompositeInterface ,FormItemInterface
    var CompositeImpl = function(){
    };
    // 2.3 实现接口的方法
    CompositeImpl.prototype.add = function(obj){
    alert("add...");
    };
    CompositeImpl.prototype.remove = function(obj){
    alert("remove...");
    };
    CompositeImpl.prototype.select = function(obj){
    alert("select...");
    };
    //在这里少实现一个方法 下面检测是否全部实现了接口方法
    // CompositeImpl.prototype.update = function(obj){
    // alert("update...");
    // };
    // 实例化 实现接口的对象
    var c = new CompositeImpl();
    //3 检验接口里的方法是否全部实现
    // 如果检验通过 继续执行;如果不通过抛出异常;
    Interface.ensureImplements = function(obj){
    // 如果接收到参数小于2 说明 传参出错了,只传入一个参数,,没有传入实现的接口
    if(arguments.length<2){
    throw new Error("接口检查方法的参数必须多余两个!");
    }
    //获得要见测的接口实现对象之后的参数 各个接口
    for(var i = 1,len = arguments.length;i<len;i++){
    var instanceInterface = arguments[i]; //获取当前这个接口
    //判断接收到的是不是接口的对象 如果不是 抛出异常
    if( instanceInterface.constructor !== Interface){
    throw new Error("接口检测函数必须传入接口对象!");
    }
    //检查实例化接口的对象是不是实现了接口里的所有方法
    // 当前接口对象里的每一个方法
    for(var j = 0 ; j<instanceInterface.methods.length;j++){
    var methodName = instanceInterface.methods[j]; //接收到了字符串的方法名
    //如果obj里面没有有methodName这个方法 或者有这个属性但是不是函数 就抛出异常
    if(!obj[methodName] || typeof obj[methodName] !== "function"){
    throw new Error("接口方法"+ methodName +"没有实现!");
    }
    }
    }
    };
    //传入要检查的类,和他要实现的所有接口对象
    Interface.ensureImplements(c ,CompositeInterface ,FormItemInterface );
    c.add();