接口
什么是接口
接口实现了一个对象应该具有那些方法的手段。
在JavaScript中模仿接口
用注释模仿接口
/*
interface Composite {
function add(child);
function remove(child);
function getChild(index);
}
interface FormItem {
function save();
}
*/
var CompositeForm = function(id, method, action){ // implements Composite, FormItem
};
// implement the Composite interface 实现接口
CompositeForm.prototype.add = function(child){
...
};
CompositeForm.prototype.remove = function(child){
...
};
CompositeForm.prototype.getChild = function(index){
...
};
// Implement the FormItem interface.
CompositeForm.prototype.save = function(){
...
};
这种模仿接口的方式,容易实现,不需要额外的类或函数,但是不会报错,对测试和调试没有帮助。
用属性检测模仿接口
这种方式更为严谨。所有类都明确声明自己实现了声明接口,那些想与这些类打交道的对象可以针对这些声明进行检查。接口本身仍然只是注释,单可以通过一个属性得知某个类实现声明接口:
/*
interface Composite {
function add(child);
function remove(child);
function getChild(index);
}
interface FormItem {
function save();
}
*/
var CompositeForm = function(id, method, action){
this.implementsInterfaces = ["Composite", "FormItem"];
...
};
function addForm(formInstance){
if(!implements(formInstance, "Composite", "formItem")){
throw new Error("Object does not implement a required interface.");
}
...
}
// The implements function, which checks to see if an object declares that it
// implement the required interfaces.
function implements(object){
for(var i = 1; i < arguments.length; i++){ // Looping through arguments
// after the first one.
var interfaceName = arguments[i];
var interfaceFound = false;
for(var j = 0; j < object.implementsInterfaces.length; j++){
if(object.implementsInterface[j] == interfaceName){
interfaceFound = true;
break;
}
}
if(!interfaceFound){
return false; // An interface was not found /
}
}
return true; // all interface were found.
}
用鸭式辨型模仿接口
类是否声明自己支持哪些借口并不重要,只要他具有这些接口中的方法就行。
把对象实现的方法集作为判断它是不是某个类的实例的唯一标准。这种方法背后的观点很简单:如果对象具有与接口定义的方法同名的所有方法,那么就可以认为它实现了这个接口。
// Interfaces.
var Composite = new Interface("Composite", ["add", "remove", "getChild"]);
var FormItem = new Interface("FormItem", ["save"]);
// CompositeForm class
var CompositeForm = function(id, method, action){
...
};
...
function addForm(formInstance){
ensureImplements(formInstance, Composite, FormItem);
// This function will throw an error if a required method is not implemented.
...
}
与前两种方法不同,这种方法不借助于注释。尽管该方法是最有用的一种,他还需要使用一个辅助类(Interface)和一个辅助函数(ensureImplements)。
本书采用的接口实现方法
综合使用第一种和第三种方法。使用注释声明类支持的接口,从而提高代码的可用性及文档的完善性。
// Interfaces.
var Composite = new Interface("Composite", ["add", "remove", "getChild"]);
var formItem = new Interface("FormItem", "save");
// Composite class
var CompositeForm = function(id, method, action){ // implements Composite, FormItem
...
};
...
function addForm(formInstance){
Interface.ensureImplements(formInstance, Composite, FormItem);
// This function will throw an error if a required method is not implemented,
如果必须的方法没有执行,这个函数将抛出一个错误
// halting execution of the function.
停下执行函数
// All code beneath this line will be executed only if the checks pass.
只要这个检查通过,这条线下面的所有代码将被执行
...
}
Interface类
下面是本书使用的 Interface类的定义:
// Constructor
var Interface = function(name, methods){
// 参数的数量必须精确为2个,否则将抛出错误
if(arguments.length !=2){
throw new Error("Interface constructor called with" + arguments.length + "arguments, but expected exactly 2.");
}
this.name = name;
this.methods = [];
for(var i = 0, len = methods.length; i < len; i++){
// 如果方法名字不是字符串抛出错误
if(typeof methods[i] !== "string"){
throw new Error("Interface constructor expected method names to be " + "passed in as a string.");
}
this.methods.push(methods[i]);
}
};
// Static class method
Interface.ensureImplements = function(object){
// 参数数量至少为2个,否则抛出错误
if(arguments.length < 2){
throw new Error("Function Interface.ensureImplements called with " + arguments.length + "arguments, but expected at lease 2.");
}
for(var i = 1, len = arguments.length; i < len; i++){
var interface = arguments[i];
if(interface.constructor !== Interface){
throw new Error("Function Interface.ensureImplements expected arguments" + "two and above to be instances of Interface.");
}
for(var j = 0, methodsLen = interface.methods.length; j < methodsLen; j++){
var method = interface.methods[j];
if(!object[method] || typeof object[method] !== "function"){
throw new Error("Function Interface.ensureImplements: object " + "does not implement the " + "interface.name + interface.Method" + method + "was not found.");
}
}
}
};
Interface 类的用法
判断在代码中使用接口是否划算,对于小型项目不需要使用接口。使用说明如下:
- 将 Interface类纳入html文件
- 逐一检查所有已对象为参数的方法
- 为需要的每个不停的方法创建一个 Interface对象
- 剔除所有针对构造器的显式检查
- 以 Interface.ensureImplements取代原来的构造器检查。
依赖于接口的设计模式
下面这些设计模式尤其依赖接口。
- 工厂模式
- 组合模式
- 装饰者模式
- 命令模式