[设计模式]创建接口

写在前面:

      在有许多程序员参与的大型项目中,接口起着至关重要的作用。程序员常常需要使用还未编写的出来的API。或者需要提供一些占位代码以免延误开发进度。接口在这种场合中的重要性表现在许多方面。它们记载着API,可作为程序员正式交流的工具。在占位代码被替换为最终的API时,你立刻就能知道所需方法是否得到了实现。在开发过程中,如果API发生了变化,只要新的API实现了同样的接口,它就能完美替换原有API。

目录:

  • 用注释描述接口
  • 用属性检查模仿接口
  • 用鸭式辨型模仿接口
  • 通用的接口实现方法

在JS中使用接口

用注释描述接口

/*
 * interface Composite {
 *     function add(child);
 *     function remove(child);
 *     function getChild(child);
 * }
 * 
 * interface FormItem {
 *     function save();
 * }
 * 
 */
var CompositeForm = function() {    //implements Composite and FormItem
    //...
};

CompositeForm.prototype = {
    //Implement the interface Composite
    add: function(child) {
        //...
    },
    remove: function(child) {
        //...
    },
    getChild: function(child) {
        
    },
    //Implement this interface FormItem
    save: function() {
        
    }
}

用属性检查模仿接口

所有类都明确地声明自己都实现了哪些接口。

那些想与这些类打交道的对象可以针对这些声明进行检查

/*
 * interface Composite {
 *     function add(child);
 *     function remove(child);
 *     function getChild(child);
 * }
 * 
 * interface FormItem {
 *     function save();
 * }
 * 
 */
var CompositeForm = function(id, method) {    //implements Composite and FormItem
    //显式声明类实现哪些接口
this.implementsInterfaces = ['Composite', 'FormItem'];
    //...
};
//...
/*
 * 先判断传递的对象是否实现了需要的接口
 * 检查通过后,才能调用需要的方法
 * @param {CompositeForm} formInstance
 */
function addForm(formInstance) {
    if(!implements(formInstance, 'Composite', 'FormItem')) {
        throw new Error("对象没有实现需要的接口");
    }
    //...
}
//检查对象是否实现了提供的接口
//但并不能保证类实现了接口的所有方法
function implements(object) {
    //第2个参数开始是需要的不确定数量的接口
    for(var i=1; i<arguments.length; i++) {
        var interfaceName = arguments[i];               //接口
        var found = false;       //标识是否实现了接口
        var interfaceNames = object.implementsInterfaces; 
        for(var j=0; j<interfaceNames.length; j++) {
            if(interfaceNames[j] == interfaceName) {
                    //在接口声明的数组中找到了
                found = true;
                break;
            }
        }
        if(!found) {
            return false;        //没有实现需要的接口
        }
    }
    return true;
}

这种方法优点:它对类所实现的接口提供了文档说明。如果需要的接口不在一个类宣称支持的接口之列时抛出一个错误。

缺点:它并不能确保类真正实现了自称实现的接口。

用鸭式辨型模仿接口

它把对象实现的方法集作为判断它是不是某个类的实例的唯一标准。这种技术可以用来判断一个类是否实现了某个接口。背后的思想:如果对象有与接口定义的方法同名的所有方法,那么就可以认为它实现了这个接口。

//interfaces
var Composite = Interface('Composite', ['add', 'remove', 'getChild']);
var FormItem = Interface('FormItem', ['save']);

//CompositeForm class
var CompositeForm = function(id, method) {
    //...
};

function addForm(formInstance) {
    //检查接口的方法formInstance对象是否都实现了
    ensureImplements(formInstance, 'Composite', 'FormItem');
    //...
}

类没有声明自己实现了哪些接口,这降低了代码的可重用性。

通用接口实现方法

var Composite = Interface('Composite', ['add', 'remove', 'getChild']);
var FormItem = Interface('FormItem', ['save']);

//CompositeForm class
var CompositeForm = function(id, method) {      //implements Composite and FormItem
    //...
};

function addForm(formInstance) {
    //检查接口的方法formInstance对象是否都实现了
    Interface.ensureImplements(formInstance, 'Composite', 'FormItem');
    //...
}

上面使用接口(Interface)类实现

/**
 * 定义接口的辅助函数
 * @param {String} name 接口名
 * @param {Array} methods 所有方法组成的数组
 */
var Interface = function(name, methods) {
    if(arguments.length != 2) {
        throw new Error('需要传递2个参数:接口名,方法名数组。');
    }
    this.name = name;
    this.methods = [];
    for(var i=0, len=methods.length; i<len; i++) {
        if(typeof methods[i] != 'string' ) {
            throw new Error('方法名应该是字符串类型');
        }
        this.methods.push(methods[i]);
    }
};

Interface.ensureImplements = function(object) {
    //判断参数的个数不少于2个
    if(arguments.length<2) {
        throw new Error('至少需要2个参数');
    }
    //检查对象的方法包含支持的接口的所有方法
    for(var i=1; i<arguments.length; i++) {
        var interface = arguments[i];           //待验证的接口
        //判断接口的类型
        if(interface.constructor !== Interface) {
            throw new Error('不是特定的接口');
        }
        var methods = interfaceName.methods;        //接口定义的所有方法
        for(var j=0, len=methods.length; j<len; j++) {
            var method = methods[j];
            if(!object[method] || typeof object[method] != 'function') {
                throw new Error('没有实现接口'+interface.name+'中的方法'+method);
            }
        }
    }
}

一个理想的软件系统应该为所有类定义接口。

    接口提供了一种用以说明一个对象应该具有哪些方法的手段。尽管它可以表明(暗示)这些方法的作用,但它并不规定这些方法应该如何实现。有了接口,可以按对象提供的特性对它们进行分组。

    JS编写接口使用中的最大问题在于,无法强迫其他程序员遵守你定义的接口。

使用接口的难点在于判断是否必要使用它,因为它并不总是不可或缺的,还降低效率

原文地址:https://www.cnblogs.com/mackxu/p/2941325.html