我们知道,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 类中通过动态原型来实现接口中定义的方法。以下是简单的几行测试代码:
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 formItem = new Interface("formItem", ["save"]);
至于compositeForm类自身的实现,除了不再需要implementsInterfaces签名,其他和之前的几乎一样。使用时,通过调用:
throw new Error("Object doesn't implement the interfaces.");
}
来判断是否真的实现了指定接口。它不仅可以判断到接口,还可以判断到方法。
至此,不仅可以定义interface,还可以ensure implement了。 (注:以上方法主题思路来源于Pro Javascript Design Pattern,但同时修复了它里面的一些细小问题) 完整代码download