目录
接口是面向对象JavaScript程序员的工具箱中最有用的工具之一。
接口是面向对象JavaScript程序员的工具箱中最有用的工具之一。
GoF在《设计模式》一书中提出的可重用面向对象设计的第一条原则中就说道:“针对接口而不是实现编程”,这个概念的基本性由此可见一斑。
问题在于,JavaScript中没有内置的创建或实现接口的方法。它也没有内置的方法可以用于判断一个对象是否实现了与另一个对象相同的一套方法,这使对象很难互换使用。好在JavaScript有着出色的灵活性,因此添加这些特性并非难事。
本章将考察其他面向对象的语言中实现接口的方法,并对它们在这方面最突出的特性进行模仿。我们先讨论在JavaScript中实现接口的各种方法,最后设计出一个可重用的类,用于检查对象是否具有必要的方法。
2.1 什么是接口
接口提供了一种用以说明一个对象应该具有哪些方法的手段。尽管它可以表明(或至少是暗示)这些方法的语义,但它并不规定这些方法应该如何实现。
例如,如果一个接口包含有一个名为setName的方法,那么你有理由认为这个方法的实现应该具有一个字符串参数,并且会把这个参数赋给一个name变量。
有了这个工具,你就能按对象提供的特性对它们进行分组。例如,即使一批对象彼此存在着极大的差异,只要它们都实现了Comparable接口,那么在object.compare(another0bject)方法中就可以互换使用这些对象。你还可以使用接口开发不同的类之间的共同性。
如果把原本要求以一个特定的类为参数的函数改为要求以一个特定的接口为参数的函数,那么任何实现了该接口的对象都可以作为参数传递给它。这样一来,彼此不相关的对象也可以被同等对待。
2.1.1 接口之利
在面向对象的JavaScript中,接口有些什么作用呢?既定的一批接口具有自我描述性,并能促进代码的重用。
接口可以告诉程序员一个类实现了哪些方法,从而帮助其使用这个类。如果你熟悉一个特定的接口,那么就已经知道如何使用任何实现了它的类,从而更有可能重用现有的类。
接口还有助于稳定不同的类之间的通信方式。如果事先知道了接口,你就能减少在集成两个对象的过程中出现的问题。借助于它,你可以事先就说明你希望一个类具有哪些特性和操作。
一个程序员可以针对所需要的类定义一个接口,并把它转交给另一个程序员。第二个程序员可以随心所欲地编写自己的代码,只要他定义的类实现了那个接口就行。这在大型项目中尤其有用。
测试和调试因此也能变得更轻松。在JavaScript这种弱类型语言中,类型不匹配错误很难跟踪。使用接口可以让这种错误的查找变得更容易一点,因为此时如果一个对象不像所要求的类型,或者没有实现必要的方法,那么你会得到包含有用信息的明确的错误提示。
这样一来,逻辑错误可以被限制在方法自身,而不是在对象的构成之中。接口还能让代码变得更稳固,因为对接口的任何改变在所有实现它的类中都必须体现出来。
如果接口添加了一个操作,而某个实现它的类并没有相应地添加这个操作,那么你肯定会立即见到一个错误。
2.1.2 接口之弊
接口并非没有缺点。JavaScript是一种具有极强表现力的语言,这主要得益于其弱类型的特点。而接口的使用则在一定程度上强化了类型的作用。这降低了语言的灵活性。
JavaScript并没有提供对接口的内置支持,而试图模仿其他语言内置的功能总会有一些风险。JavaScript中没有Interface这个关键词,因此,不管你用什么方法实现接口,它总是与C++和Java这些语言中所用的方法大相径庭,这加大了初涉JavaScript时所遇到的困难。
JavaScript中任何实现接口的方法都会对性能造成一些影响,在某种程度上这得归咎于额外的方法调用的开销。
我们的实现方法中使用了两个for循环来遍历所需要的每一个接口中的每一个方法。对于大型接口和需要实现许多不同接口的对象,这种检查可能要花点时间,从而对性能造成负面影响。
如果你在乎这个问题,那么可以在开发完成之后剔除这种代码,或者将其执行与一个调试标志关联起来,这样在运营环境中它就不会执行。但要注意不要过早进行优化处理。Firebug这类性能分析器可以帮助你判断是否真有必要剔除接口代码。
接口使用中的最大问题在于,无法强迫其他程序员遵守你定义的接口。在其他语言中,接口的概念是内置的,如果某人定义了实现一个接口的类,那么编译器会确保该类的确实现了这个接口。而在JavaScript中则必须用手工的办法保证某个类实现了一个接口。
编码规范和辅助类可以提供一些帮助,但无法彻底根除这个问题。如果项目的其他程序员不认真对待接口,那么这些接口的使用是无法得到强制性保证的。除非项目的所有人都同意使用接口并对其进行检查,否则接口的很多价值都无从体现。
2.2 其他面向对象语言处理接口的方式
这里先概览一下三种广泛使用的面向对象语言处理接口的方式。你会发现它们的办法大体相似。稍后在2.5节中创建Interface类时,我们会尽量模仿它们的功能。
Java使用接口的方式是面向对象语言中比较典型的,所以我们先从Java开始。下面是java.i0包中的一个接口:
public interface DataOutput{
void writeBoolean(boolean value) throws IOxception;
void writeByte(int value)throws IException;
void writeChar(int value)throws IOException;
void writeShort(int value)throws IOException;
void writeInt(int value)throws IOException;
}
它列出了一个类应该实现的一批方法,包括方法的参数和可能会抛出的异常。每一行都像是一个方法声明,只不过是以一个分号而不是一对大括号结尾的。
创建一个实现这个接口的类需要使用关键字implements:
public class Data0utputStream extends FilterOutputStream i