您的位置:首页 > Web前端 > JavaScript

JavaScript 面向对象设计---接口

2010-10-11 12:42 253 查看
接口在面向对象编程中是非常重要的概念/工具,早在四人帮(GOF)的DesignPattern中提到面向对象编程的其中一规则是“面向接口编程,而不要面向实现编程”。--这可见接口在可复用的面向对象设计中的重要性吧~

对于面向对象JavaScript而言,实现接口并不简单,因为JavaScript并没有内建支持接口。幸运的是,我们可以利用JavaScript的灵活性来模拟实现接口。在模拟接口前,首先了接一下其他面向对象如何定义/处理接口:

1.Java

publicinterfaceDataOutput{

voidwriteBoolean(booleanvalue)throwsIOException;

voidwriteByte(intvalue)throwsIOException;

voidwriteChar(intvalue)throwsIOException;

voidwriteShort(intvalue)throwsIOException;

voidwriteInt(intvalue)throwsIOException;

...

}

DataOutputStreamClass通过关键字implements实现了DataOutput接口

11publicclassDataOutputStreamextendsFilterOutputStreamimplementsDataOutput{

2
32publicfinalvoidwriteBoolean(booleanvalue)throwsIOException{
4
53write(value?1:0);
6
74}
8
95...

6}

在接口定义的每一个方法都必须在实现类中实现。如果某一个方法没有被实现,编译的时候会出现一些错误:
DataOutputStreamshouldbedeclaredabstract;itdoesnotdefinewriteBoolean(boolean)inDataOutputStream.

2.PHP:

interfaceMyInterface{ publicfunctioninterfaceMethod($argumentOne,$argumentTwo); } classMyClassimplementsMyInterface{ publicfunctioninterfaceMethod($argumentOne,$argumentTwo){ return$argumentOne.$arguemntTwo; } } classBadClassimplementsMyInterface{ //Nomethoddeclarations. } //BadClasscausesthiserroratrun-time: //Fatalerror:ClassBadClasscontains1abstractmethodsandmustthereforebe //declaredabstract(MyInterface::interfaceMethod)

3C#:

interfaceMyInterface{ stringinterfaceMethod(stringargumentOne,stringargumentTwo); } classMyClass:MyInterface{ publicstringinterfaceMethod(stringargumentOne,stringargumentTwo){ returnargumentOne+argumentTwo; } } classBadClass:MyInterface{ //Nomethoddeclarations. } //BadClasscausesthiserroratcompile-time: //BadClassdoesnotimplementinterfacememberMyInterface.interfaceMethod()

从上面不同的面向对象语言中可以看出,它们定义/处理接口的方式都差不多:

1.接口定义了需要实现什么方法,这些方法需要什么参数;2.而实现类则显式声明接口中定义的方法并实现之。3.如果实现类没有把接口中所有的方法声明并实现之,则报错。

当然,在JavaScript的面向对象编程中不能像上面的编程语言来定义,使用接口,因为javaScript中没有interface,implements等关键字,也没有编译的错误检查。

在JavaScript中模拟接口

我们可以用以下三种方式来在JavaScript模拟接口:

1.注释法2.属性检测法3特征法

用注释的方法实现接口

/* interfaceComposite{ functionadd(child); functionremove(child); functiongetChild(index); } interfaceFormItem{ functionsave(); } */ varCompositeForm=function(id,method,action){//implementsComposite,FormItem ... }; //ImplementtheCompositeinterface. CompositeForm.prototype.add=function(child){ ... }; CompositeForm.prototype.remove=function(child){ ... }; CompositeForm.prototype.getChild=function(index){ ... }; //ImplementtheFormIteminterface. CompositeForm.prototype.save=function(){ ... };
通过上面例子看到,我们用面向对象的方式定义了接口,并把它放置到注释中。这样,既不产生错误,又起了接口定义的作用。不过,这种方法还不能很好地模拟出接口,因为它没有检测机制--它不能保证,也不知道CompositeForm是否实现了Composite接口中的所有方法。不过这种方法的好处也很明显--简单,而且不需要额外的帮助类或者方法来支持。

用属性检测来实现接口

/* interfaceComposite{ functionadd(child); functionremove(child); functiongetChild(index); } interfaceFormItem{ functionsave(); } */ varCompositeForm=function(id,method,action){ //声明必须实现'Composite','FormItem'接口
this.implementsInterfaces=['Composite','FormItem']; ... }; ... functionaddForm(formInstance){
if(!implements(formInstance,'Composite','FormItem')){ thrownewError("Objectdoesnotimplementarequiredinterface."); } ... } //Theimplementsfunction,whichcheckstoseeifanobjectdeclaresthatit
//接口检测 //implementstherequiredinterfaces. functionimplements(object){ for(vari=1;i<arguments.length;i++){//Loopingthroughallarguments //afterthefirstone. varinterfaceName=arguments[i]; varinterfaceFound=false; for(varj=0;j<object.implementsInterfaces.length;j++){ if(object.implementsInterfaces[j]==interfaceName){ interfaceFound=true; break; } } if(!interfaceFound){ returnfalse;//Aninterfacewasnotfound. } } returntrue;//Allinterfaceswerefound. }
从上面的例子看到,CompositeForm中声明了该类必须要实现composite,formitem接口,检测没有实现,则抛出Error。

这种实现方式的优点如下:

1.记录了该类实现了哪几个接口。

2.如果该类没有实现它声明的接口,则会报错,这样,我们就可以强制性地让其他程序员必须实现这些接口。

但是,这种实现方式的缺点也很明显:

我们保证了实现类必须声明接口,但不能保证实现类一定实现了接口的所有方法。

3.特征法

什么是特征法呢?打个比方,如果我们看见一种动物它走起来像鸭子,叫声像鸭子,生活习性也像鸭子,那么我们就认定它就是鸭子。用编程的角度来讲就是:如果一个类它声明了接口,而且实现了接口的所有方法,我们就认定了它实现了这些接口。容易理解吧~看例子:

//Interfaces. varComposite=newInterface('Composite',['add','remove','getChild']); varFormItem=newInterface('FormItem',['save']); //CompositeFormclass varCompositeForm=function(id,method,action){
//implementsComposite,FormItem’smethod ... }; ... functionaddForm(formInstance){ Interface.ensureImplements(formInstance,Composite,FormItem); //Thisfunctionwillthrowanerrorifarequiredmethodisnotimplemented, //haltingexecutionofthefunction. //Allcodebeneaththislinewillbeexecutedonlyifthecheckspass. ... }

/**Interfaceclass**/
1//Constructor.
2varInterface=function(name,methods){
3if(arguments.length!=2){
4thrownewError("Interfaceconstructorcalledwith"+arguments.length+
5"arguments,butexpectedexactly2.");
6}
7this.name=name;
8this.methods=[];
9for(vari=0,len=methods.length;i<len;i++){
if(typeofmethods[i]!=='string'){
thrownewError("Interfaceconstructorexpectsmethodnamestobe"
+"passedinasastring.");
}
this.methods.push(methods[i]);
}
};

//Staticclassmethod.
Interface.ensureImplements=function(object){
if(arguments.length<2){
thrownewError("FunctionInterface.ensureImplementscalledwith"+
arguments.length+"arguments,butexpectedatleast2.");
}
for(vari=1,len=arguments.length;i<len;i++){
varinterface=arguments[i];
if(interface.constructor!==Interface){
thrownewError("FunctionInterface.ensureImplementsexpectsarguments"
+"twoandabovetobeinstancesofInterface.");
}
for(varj=0,methodsLen=interface.methods.length;j<methodsLen;j++){
varmethod=interface.methods[j];
if(!object[method]||typeofobject[method]!=='function'){
thrownewError("FunctionInterface.ensureImplements:object"
+"doesnotimplementthe"+interface.name
+"interface.Method"+method+"wasnotfound.");
}
}
}
};
上面的例子中,帮助类Interface约束了实现类必须声明接口,实现接口的方法。如果读者打算用设计模式来开发复杂的系统,我想这种方式会带来很大的好处。因为,对于大型的系统而言,会有很多程序员会一起参与开发,怎样让程序员更容易交流?代码质量更高?效率更高呢?当然就是规范了,接口就定义了一系列规范,其作用不言而喻。

下面再提供一个使用接口的范例:

使用接口前的代码:

//该类持有一个TestResultObject,并有了一个renderResults方法把测试结果输出到页面
varResultFormatter=function(resultsObject){
if(!(resultsObjectinstanceOfTestResult)){
thrownewError("ResultsFormatter:constructorrequiresaninstance"
+"ofTestResultasanargument.");
}
this.resultsObject=resultsObject;
};
ResultFormatter.prototype.renderResults=function(){
vardateOfTest=this.resultsObject.getDate();
varresultsArray=this.resultsObject.getResults();
varresultsContainer=document.createElement('div');
varresultsHeader=document.createElement('h3');
resultsHeader.innerHTML='TestResultsfrom'+dateOfTest.toUTCString();
resultsContainer.appendChild(resultsHeader);
varresultsList=document.createElement('ul');
resultsContainer.appendChild(resultsList);
for(vari=0,len=resultsArray.length;i<len;i++){
varlistItem=document.createElement('li');
listItem.innerHTML=resultsArray[i];
resultsList.appendChild(listItem);
}
returnresultsContainer;
};
使用了接口类之后的代码

//ResultSetInterface.
varResultSet=newInterface('ResultSet',['getDate','getResults']);
//ResultFormatterclass,afteraddingInterfacechecking.
varResultFormatter=function(resultsObject){
Interface.ensureImplements(resultsObject,ResultSet);
this.resultsObject=resultsObject;
};
ResultFormatter.prototype.renderResults=function(){
...
};
从上例中应该可以看出,重构后的代码可读性提高了?更重要的是,很多设计模式也依赖于接口,如:工厂模式,命令模式,油漆工模式等…接口只是我们为JavaScript设计模式铺平了道路…Amazingwillcomingsoon..Contactwithme:timo.li.icon@gmail.com
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐