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 class
var 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 interfaces
var compsite = new Interface("compsite", ["add", "remove", "getChild"]);
var formItem = new Interface("formItem", ["save"]);

// a class implements above two interfaces
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;
    }
};

// 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,但同时修复了它里面的一些细小问题) 完整代码download

原文地址:https://www.cnblogs.com/Langzi127/p/2677035.html