Javascript中如何实现interface机制

我们知道,ECMAScript中是没有interface一说的。虽然如此,参考《Pro Javascript Design Pattern》一书,哥算是找到方案了。

 

最简单的方案,就是通过文档说明了。简称方案1,代码如下:

/*interface composite{    function add(child);    function remove(child);    function getChild(index);}interface formItem{    function save();}*/var compositeForm = function (id, name) {    this.id = id;    this.name = name;    if (typeof (compositeForm._initialized) === "undefined") {        // implement the composite interface.        compositeForm.prototype.add = function (child) {            console.log("added.");        };        compositeForm.prototype.remove = function (child) {            console.log("removed.");        };        compositeForm.prototype.getChild = function (index) {            return "Here is a child.";        };        // implement the formItem interface.        compositeForm.prototype.save = function () {            console.log("saved.");        };        compositeForm._initialized = true;    }

}; 

这种方案里,通过js的伪代码,定义好composite和formItem 2个接口,然后在 compositeForm 类中通过动态原型来实现接口中定义的方法。以下是简单的几行测试代码: 

 

var item = new compositeForm(1, "form 1");console.log(item.id);item.add(null);item.save();var item2 = new compositeForm(2, "form 2");

console.log(item.add == item2.add); 

对于具有特强执行力和完全可控制的团队来说,完全没有问题。问题是,21世纪有这样的团队吗?即便有,IT圈里的人员变动也不能保证它一如既往啊。改进的方案就是,通过约定和校验了。作出改进(简称方案2),详细如下: 

/*interface composite{    function add(child);    function remove(child);    function getChild(index);}interface formItem{    function save();}*/var compositeForm = function (id, name) {    this.id = id;    this.name = name;    if (typeof (compositeForm._initalized) === "undefined") {        // implement the composite interface.        compositeForm.prototype.add = function (child) {            console.log("added.");        };        compositeForm.prototype.remove = function (child) {            console.log("removed.");        };        compositeForm.prototype.getChild = function (index) {            return "Here is a child.";        };        // implement the formItem interface.        compositeForm.prototype.save = function () {            console.log("saved.");        };        //sign for implemented interfaces        compositeForm.prototype.implementsInterfaces = ["composite", "formItem"];        compositeForm._initalized = true;    }};function implements(obj, interfaces) {    if (obj == null || typeof (obj) !== "object") {        throw new Error("obj must to be a object.");    }    if(interfaces == null || !(interfaces instanceof Array)){        throw new Error("interfaces must to be a Array");    }    for(item in interfaces){        if(typeof(interfaces[item]) !== "string"){            throw new Error("interfaces must to be a string Array");        }    }    var result = true;    if (interfaces.length > 0) {        if (typeof (obj.implementsInterfaces) === "undefined" || !(obj.implementsInterfaces instanceof Array) || obj.implementsInterfaces.length < 1) {            result = false;        } else {            for (item in interfaces) {                var itemResult = false;                for (funIndex in obj.implementsInterfaces) {                    if (interfaces[item] == obj.implementsInterfaces[funIndex]) {                        itemResult = true;                        break;                    }                }                if (!itemResult) {                    result = false;                    break;                }            }        }    }    return result;    }// Validate instace. If invalid, throw exception.function addForm(formInstance) {    if (!implements(formInstance, ["composite", "formItem"])) {        throw new Error("Object doesn't implement the interfaces.");    }    //...

它的核心就是:compositeForm.prototype.implementsInterfaces = ["composite", "formItem"];通过定义implementsInterfaces属性,并将它赋值为已实现的接口名称的数组。而implements方法就是通过对比对象实体的implementsInterfaces属性和需要实现的接口名称逐一进行对比,来判断实体所属类是否真的实现了指定接口。但是,如果给implementsInterfaces赋值说已实现了某接口,但是并没有实现它的方法,怎么办?

     继续改进,有方案3

/*interface composite{    function add(child);    function remove(child);    function getChild(index);}interface formItem{    function save();}*/// interface classvar Interface = function (name, methods) {    if (arguments.length != 2) {        throw new Error("Interface constructor called with " + arguments.length + "arguments, but expected exactly 2.");    }    if (typeof name !== "string") {        throw new Error("Interface constructor expected name to be passed in as a string.");    }    if (methods == null || !(methods instanceof Array)) {        throw new Error("Interface constructor expected methods to be passed in as a method.");    }    this.name = name;    this.methods = methods;};Interface.ensureImplements = function (classFun, interfaces) {    if (arguments.length != 2) {        throw new Error("Function Interface.ensureImplements called with " + arguments.length + "arguments, but expected 2.");    }    if (classFun == null || typeof (classFun) != "object") {        throw new Error("classFun expected to be passed in a object.");    }    if (interfaces == null || !(interfaces instanceof Array)) {        throw new Error("interfaces expected to be passed in a Array.");    }    for (index in interfaces) {        if (!(interfaces[index] instanceof Interface)) {            throw new Error("interfaces[" + index + "] expected to be passed in a Interface.");        }        var currentInterface = interfaces[index];        for (methodIndex in currentInterface.methods) {            var methodName = currentInterface.methods[methodIndex];            if (!classFun[methodName] || typeof (classFun[methodName]) != "function") {                return false;            }        }    }    return true;};// define two interfacesvar compsite = new Interface("compsite", ["add", "remove", "getChild"]);var formItem = new Interface("formItem", ["save"]);// a class implements above two interfacesvar compositeForm = function (id, name) {    this.id = id;    this.name = name;    if (typeof (compositeForm._initialized) === "undefined") {        // implement the composite interface.        compositeForm.prototype.add = function (child) {            console.log("added.");        };        compositeForm.prototype.remove = function (child) {            console.log("removed.");        };        compositeForm.prototype.getChild = function (index) {            return "Here is a child.";        };        // implement the formItem interface.        compositeForm.prototype.save = function () {            console.log("saved.");        };        compositeForm._initialized = true;    }};// Validate instace. If valid, alert true. Else, throw exception. function addForm(formInstance) {    if (!Interface.ensureImplements(formInstance, [compsite, formItem])) {        throw new Error("Object doesn't implement the interfaces.");    }    //...    alert('true');}

这里定义了Interface类,并实现了类的ensureImplements静态方法。关于Interface类的实现,可以独立作为一个文件,以后多个地方可以用到。然后,通过下面的代码来定义接口: 

var compsite = new Interface("compsite", ["add", "remove", "getChild"]);var formItem = new Interface("formItem", ["save"]);

至于compositeForm类自身的实现,除了不再需要implementsInterfaces签名,其他和之前的几乎一样。使用时,通过调用: 

    if (!Interface.ensureImplements(formInstance, [compsite, formItem])) {        throw new Error("Object doesn't implement the interfaces.");

    } 

来判断是否真的实现了指定接口。它不仅可以判断到接口,还可以判断到方法。 

     至此,不仅可以定义interface,还可以ensure implement了。 (注:以上方法主题思路来源于Pro Javascript Design Pattern,但同时修复了它里面的一些细小问题) 完整代码

 

36人参与, 0条评论 登录后显示评论回复

你需要登录后才能评论 登录/ 注册