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