




已阅读5页,还剩5页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第二章 接口接口是面向对象的JS程序员工具箱中最有用的工具之一,在四人组的设计模式中关于可重用的面向对象设计思想的第一原则就说:面向接口编程,而不要面向实现编程。这告诉你这个概念有多么重要。但问题是JS没有内建的方式去实现接口。它也没有内建的方法去确定一个对象实现了和另一个对象相似的方法集合。这造成了对象不能交换使用。但所幸JS是一门扩展性非常强的语言,这使得它可以很简单的增加这些特性。这一章,我们先看其他语言是如何实现接口的,然后来尝试模仿它们的精华。看下利用JS如何实现这些。当然,我们最后会提出一个可以检查对象是否含有所需方法的可重用的类。什么是接口接口提供了那些方法是对象必须的。它不指定如何实现这些方法,尽管他也许包含(或暗含)了方法的含义,例如,如果一个接口包含了一个setName方法,你理所当然的就会确定这个方法的实现就是接受一个字符串参数,然后将其指定给一个name变量。根据对象提供的特性来作为它们分组的依据。例如,如果一类看上去存在很大差异的对象都实现了Comparable接口,那它们就可以交替使用到pare(anotherObject)上。这允许两个不同的类之间建立对话。将一个接受类作为参数的函数可改为接受一个接口作为函数的参数,这就允许将任何具体实现的类传递进去。一些没有联系的类被允许同等的对待。使用接口的好处接口在JS的面向对象中可以做什么?建立一个接口可以起到自我说明和提升重用性的功效。接口告诉程序员一个更易用的类需要实现哪些方法。如果你熟悉一个接口,你就已经知道了用任何一个存在的类去实现它,提升已存在类可重用的机会。接口使得不同类之间的沟通更加稳固。提前知道接口,你就可以减少集成两个对象时出现的错误。接口允许你提前指定一个类需要有哪些特性和操作。一个程序员因为需要为某个类编写了一个接口,然后把它交给了另外一个程序员。第二个程序员一旦实现了这个接口,那无论用什么方法,这个类都会正常运转。这在大型工程中尤为有效。检测和调试变得更加简单。在类似与JS这样的弱类型语言中,跟踪类型匹配错误非常困难。但使用接口就会让这变得简单一些,因为如果一但某个类没有所期望的类型或者没有实现某个需求的接口,显式的错误就会给出。然后逻辑错误被限制在了它们自己的方法中,而不是对象之间中。接口也保证了你代码的重用性更加稳定,因为所有的类必须随着所实现接口的改动而改动。如果你为某个接口增加了一个操作,而没有为实现这个接口的类增加相应的操作,那错误马上就会出现。使用接口的缺点。使用接口并不是没有缺点。JS之所以是一门非常灵活的语言,在很大程度上是由于它的弱类型。使用接口就意味着强类型,这降低了JS的灵活性。JS没有内建的接口支持,模仿其他语言内建的机制常常带来一些风险。它没有interface关键字。所以你为用来实现接口的方法比起C+和JAVA来都显得非常困难。在JS中使用任何的接口都会使性能有所下降。因为部分的开销要用来调用另外的函数。我们的实现用了两个for循环来迭代遍历每个方法到每个请求的接口。对一些实现大型接口或很多接口的类来说,这种迭代需要花费一些时间并对系统性能造成负面影响。如果你担心这点,你可以在开发完成后跳过这一步,或这给它一个调试标志,以便其在产品环境中不执行。但必须要保证过早的优化。类似于firebug这样可以帮你检测接口是否被剥离出来的调试工具是非常必须的。最大的缺点是你无法使其他的程序员严格遵循你所编写的接口。在其他语言中interface是内建的,如果程序员为某个类实现了一个接口,编译器就会检查这个类是否真正的实现了这个接口。在JS中,你必须人工的确定给定的类是否实现了一个接口。你可以利用代码规范和辅助类来缓解这个问题,但却无法完全解决。如果其他程序员和你共同做一个项目而无视你的借口,那你也没办法强迫他们。接口在其他面向对象语言中的应用我们简短看一下三种常用的面向对象语言是如何处理接口的,你会发现他们之间非常类似。我们再“接口类”这一节中创建interface类时,会尽可能的模仿这些机制。JAVA使用了一种典型的方法来实现接口,我们先从它开始。这是java.io包中的一个接口public interface DataOutput void writeBoolean(boolean value) throws IOException;void writeByte(int value) throws IOException;void writeChar(int value) throws IOException;void writeShort(int value) throws IOException;void writeInt(int value) throws IOException;.这是一个类所必须实现的方法列表,与参数和异常联系到一起,每一行都是看起来很相似的方法声明,异常后面是分号,而不是一对大括号。创建一个使用这个借口的类,需要implements关键字public class DataOutputStream extends FilterOutputStream implements DataOutput public final void writeBoolean (boolean value) throws IOException write (value ? 1 : 0);.每一个接口中列出的方法都被声明和实现,如果某个方法没有被实现,错误就会在编译的时候显示,类似如下MyClass should be declared abstract; it does not define writeBoolean(boolean) in MyClass.PHP使用了相同的语法:interface MyInterface public function interfaceMethod($argumentOne, $argumentTwo);class MyClass implements MyInterface public function interfaceMethod($argumentOne, $argumentTwo) return $argumentOne . $arguemntTwo;class BadClass implements MyInterface / No method declarations./ BadClass causes this error at run-time:/ Fatal error: Class BadClass contains 1 abstract methods and must therefore be / declared abstract (MyInterface:interfaceMethod)还有C#interface MyInterface string interfaceMethod(string argumentOne, string argumentTwo);class MyClass : MyInterface public string interfaceMethod(string argumentOne, string argumentTwo) return argumentOne + argumentTwo;class BadClass : MyInterface / No method declarations./ BadClass causes this error at compile-time:/ BadClass does not implement interface member MyIerfaceMethod()所有这些语言都大体上使用了相同的方法。一个接口包含了那些方法需要被实现,以及这些方法需要的参数。在类中,先是声明了它要实现哪个接口,通常是使用implements关键字。每个类都可以实现不止一个接口。如果接口中的哪个方法没有被实现,错误就会抛出。根据语言的不同,这会在编译或者运行时发生。错误信息告诉用户三件事:类名,接口名,未被实现的方法名。很明显,我们不能用同样的途径实现借口,因为JS中没有interface和implements关键字。因此不能在运行时检查是否遵循接口。然而,使用辅助类和显式的遵从性检查以模拟接口的大部分特性确实可能的。在JS中模拟接口我们将讨论在JS中模拟接口的三种方法:注释,属性检查和鸭子类型,没有哪种方案是完美的,但结合这三种方法就会趋于完美。用注释描述接口最简单也是效果最差的接口模拟是使用注释。模仿其他面向对象语言的方式,在注释中使用interface和implements关键字。这无法检测语法错误,这有一个例子显示了关键字如何被添加到代码中以起到说明的作用。/*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.CompositeFtotype.add = function(child) .;CompositeFtotype.remove = function(child) .;CompositeFtotype.getChild = function(index) .;/ Implement the FormItem interface.CompositeFtotype.save = function() .;这并没有很好的模拟接口的功能,他没有检查以确保compositeForm确实实现了正确地方法集合。也没有抛出错误以通知程序员问题所在,除了说明外它没有起到任何作用。所有的一致性都要靠程序员完全自发完成。然而,这种方法也有一些优点,它容易实现,不需要额外的类或函数。提升了可重用性,因为类有了接口说明并且可以跟其他实现了相同接口的类交换。它不影响文档的大小和执行速度。当代码展开式,这些注释可以被轻易的剥离。避免了因使用接口而造成文件大小的增加。但是,由于无法给出错误。他不能帮助检查和调试代码。用属性检查模拟接口第二种方法就要严格一些了,所有类显示声明了所要实现的接口,这些声明被想要与这些类结合的对象检查,接口依然仅仅是个注释,但是你现在可可以检查一个属性,看它的类“自称”实现了一个什么接口。/*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 arequired interface.);./ The implements function, which checks to see if an object declares that it / implements the required interfaces.function implements(object) for(var i = 1; i arguments.length; i+) / Looping through all arguments / after the first one.var interfaceName = argumentsi;var interfaceFound = false;for(var j = 0; j object.implementsInterfaces.length; j+) if(object.implementsInterfacesj = interfaceName) interfaceFound = true;break;if(!interfaceFound) return false; / An interface was not found. return true; / All interfaces were found.在这个例子中,CompositeForm声明了它要实现两个接口,Composite和FormItem,这是通过把接口们的名称添加到一个数组中实现的,数组标记为implementInterfaces。这个类显示声明了它将支持那个类。任何一个使用确定的类型作为参数的函数都可以检查其属性,如果所要求的接口没有被实现,就会抛出一个错误。这种方法的优点在于,你说明了一个类实现了哪些接口,如果一个没有声明它需要实现的某个接口,就会出现错误。你可以利用这些错误强迫其它程序员声明这些接口。然而它的缺点在于你无法确定类是否真正实现了这个接口,你仅知道它“自称”实现了接口。创建一个声明了却忘了实现接口的类是非常容易的,所有的检测都会通过,但没有实现方法,这就造成了潜在的问题。当然,显示的声明类所要支持的接口无疑也增加了工作两。用鸭子类型模拟接口事实上,一个类是否声明了它所支持的接口是无关紧要的,只需要函数出现在正确的地方。这就是使用鸭子类型的合适地方。鸭子类型的意思是:“如果它走路像鸭子,叫起来也像鸭子,那它就是鸭子”。这个技巧判断对象是否为一个类的实例完全基于它实现了什么方法。这种方法的含义非常简单,如果一个对象中包含的方法和这个对象所要实现的接口中包含的方法具有相同的名称,那么它就实现了这个方法。利用辅助函数,你可以确定它是否包含相应的函数。/ Interfaces.var Composite = new Interface(Composite, add, remove, getChild);var FormItem = new Interface(FormItem, save);/ CompositeForm classvar CompositeForm = function(id, method, action) .;.function addForm(formInstance) ensureImplements(formInstance, Composite, FormItem);/ This function will throw an error if arequired method is not implemented.这种方法区别与前两种方法在于它没有注释。所有的特性都是具有强制力。ensureImplement函数需要至少两个参数,第一个参数是需要检测的对象,剩下的参数是这个对象需要比较的接口。这个函数检测了第一个参数传递进来的对象是否实现了接口中生命的方法。如果没有,则包含相关信息的错误就会被抛出。信息中包含了类名,接口名,以及没有被正确实现的方法名。这个函数可以被放置在任何需要的地方。在这个例子中,你仅需在它实现所需方法的情况下利用addForm添加表单。这是三种方法中最有效的,但它仍然有一些缺点,一个类从不声明它实现了哪谢方法。这就降低了代码的可重用性。它也没有像其他方法一样有说明。它还需要一个辅助类interface和一个辅助函数ensureImplement。它没有检查方法的参数数量和名称。也没有类型检查,紧紧是检测对象是否有正确地方法名。本书中的接口实现在本书中,我们综合了第一种方法和第三种方法,用注释来声明一个类需要支持哪些接口,这提升了类的可重用性并有了说明文档。我们用Interface辅助类和Interface.ensureImplement辅助函数来执行显示检查。当对象没有通过检查,就会返回一个相关的错误信息。这是Interface类和注释相结合的例子:/ Interfaces.var Composite = new Interface(Composite, add, remove, getChild);var FormItem = new Interface(FormItem, save);/ CompositeForm classvar CompositeForm = function(id, method, action) / implements Composite, FormItem.;.function addForm(formInstance) Interface.ensureImplements(formInstance, Composite, FormItem);/ This function will throw an error if arequired method is not implemented,/ halting execution of the function./ All code beneath this line will be executed only if the checks pass.Interface.ensureImplements提供了更严格的检查,如果有问题出现,就会有异常抛出,异常可以被扑捉并处理,或者停止执行代码。总之,程序员会立即知道问题出现并加以处理。接口类下面就是我们在本书中使用的Interface类/ Constructor.var Interface = function(name, methods) if(arguments.length != 2) throw new Error(Interface constructor called with + arguments.length +arguments, but expected exactly 2.); = name;this.methods = ;for(var i = 0, len = methods.length; i len; i+) if(typeof methodsi != string) throw new Error(Interface constructor expects method names to be + passed in as astring.);this.methods.push(methodsi); ; / Static class method.Interface.ensureImplements = function(object) if(arguments.length 2) throw new Error(Function Interface.ensureImplements called with + arguments.length + arguments, but expected at least 2.);for(var i = 1, len = arguments.length; i len; i+) var interface = argumentsi;if(interface.constructor != Interface) throw new Error(Function Interface.ensureImplements expects arguments+ two and above to be instances of Interface.);for(var j = 0, methodsLen = interface.methods.length; j methodsLen; j+) var method = interface.methodsj;if(!objectmethod | typeof objectmethod != function) throw new Error(Function Interface.ensureImplements: object + does not implement the + + interface. Method + method + was not found.); ;什么时候使用接口类严格的类型检查并非总是必须,许多JS程序员经年也不曾使用一个接口或着类型检查。但是当你使用接口去完成一个复杂的系统时,这会变得非常重要。接口看上去似乎降低了JS的灵活性,但实际上灵活性反而得到了提高,因为类之间的耦合变得更加松散。函数也变得更加灵活,因为在你为函数传递进任何类型的参数后,依然能使正确地对象使用正确的方法,下面几中情形中接口非常有用在多名程序员合作的大型工程中,接口是必需的,通常程序员所需要的API还没有写完,或者需要提供占位程序以保证工程的正常进度。接口在这种情况下变得非常有价值。它们说明了可用于两个程序员之间沟通的API,当占位程序被正式的API所替代时。你立即就会知道你所需要的方法是否会被实现。如果API在中期开发中改变,另外一个程序员也可以实现相同的接口来无缝的替代。包含来自互联网的那些没有控制权的代码越来越普遍,外部主机库是其中的一个例子,如搜索,电邮和地图这样的服务API,尽管它们的来源可靠,也要注意确保它们的改变不要导致代码错误。解决途径之一是为你以来的这些API创建Interface对象,然后检测每个对象是否正确的实现了它们的接口。var DynamicMap = new Interface(DynamicMap, centerOnPoint, zoom, draw);function displayRoute(mapInstance) Interface.ensureImplements(mapInstace, DynamicMap);mapInstance.centerOnPoint(12, 34);mapInstance.zoom(5);mapInstance.draw();.在这个例子中,displayRoute函数需要传递进来的参数对象拥有三个指定的方法,使用Interface对象并调用Interface.ensureImplement方法就可以确定这三个方法是否被实现,如果没有则出现错误。错误可以被一个try/catch语句扑捉到,然后在后台利用AJAX请求提醒你这个外部API的错误。这可以使你的Mash-ups(指通过多源头信息整合,轻松创建一项新服务)变得更加健壮和隐秘。如何使用接口类最重要的一步(也是最难执行的一步)是确定在你的代码中是否值得使用接口。难度简单的项目也许从接口那里得不到什么好处。你得自己权衡利弊。假如用了,下面提供如何使用接口的方法:1. 在HTML文件中包含Interface类。Interface.js文件可以在网站/获取。2. 遍历并找到找到你的代码中用对象做参数的函数。找出这些对象需要那些方法才能使你的代码正常工作3. 为每个方法集合谨慎的创建接口4. 移除所有显示的结构体检查。因为我们用了鸭子类型,对象的类型已经无关紧要。5. 用Interface.ensureImplement替换结构体检查。你从这里面得到了什么?你的代码变得更加松耦合因为你不再依赖任何类的实例。相反的你所需要的特性在合适的地方,任何具体的实现都会被使用。给你更多的自由去优化和重构你的代码。实例:使用接口类想象一下你创建了一个类,用来自动检测结果并格式化输出为一个网页视图。这个类的构造函数用了一个TestResult类的实例作为参数。然后他将数据压缩后传入TestResult对象并将其传递给请求。这便是ResultFormatter最初的样子。/ ResultFormatter class, before we implement interface checking.var ResultFormatter = function(resultsObject) if(!(resultsObject instanceOf TestResult) throw new Error(ResultsFormatter: constructor requires an instance + of TestResult as an argument.);this.resultsObject = resultsObject;ResultFtotype.renderResults = function() var dateOfTest = this.resultsObject.getDate();var resultsArray = this.resultsObject.getResults();var resultsContainer = document.createElement(div);var resultsHeader = document.createElement(h3);resultsHeader.innerHTML = Test Results from + dateOfTest.toUTCString();resultsContainer.appendChild(resultsHeader);var resultsList = document.createElement(ul);resultsContainer.appendChild(resultsList);for(var i = 0, len = resultsArray.length; i len; i+) var listItem = document.createElement(li);listItem.innerHTML = resultsArrayi;resultsList.appendChild(listItem);return resultsContainer;这个类在构造函数中执行了一个检查以保证参数确实为TestResult的实例;如果不是,抛出异常。这可以让你代码中的renderResults方法知道getDate方法和getResults方法是可用的。是这样吗,在构造函数中,你仅仅检查了resultsObject 是TestResult的实例。这并不能保证你所需要的方法被实现了。TestResult可以被修改并不再拥有一个getDate方法。这个检查会通过,但renderResults就会失效。结构体中的检查还有不必要的限制。它禁止了其它类的实例作为参数,即使它们能很好的运行。例如你有一个WeatherData类。它有getDate和getResults方法并可以在ResultFormatter中运行而没有任何问题。但是用显示的类型检查(用instanceOf操作符)就会禁止WeatherData的任何实例。解决办法是移除instanceOf检查并用接口代替。第一步是创建接口本身:/ R
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025版隔层施工与室内装修一体化合同范本
- 二零二五版高端食品储藏室租赁及冷链服务协议
- 2025版影视版权转让居间代理合同
- 2025版新能源储能技术研发与合作协议执行步骤
- 二零二五年度二手房买卖定金合同含房屋交易税费减免承诺
- 二零二五年度城市夜景亮化项目厂家直销供货合同模板
- 2025版婚庆现场礼仪服务与执行合同
- 二零二五年度智慧家居安装承包合同模板
- 二零二五年度仓库节能减排改造合同
- 2025版高性能计算机系统集成与维护服务合同
- 2025年西藏自治区事业单位招聘考试教师招聘体育学科专业知识试卷(模拟试题)
- 先天性甲状腺功能减退症诊治指南解读课件
- 2025至2030中国裸眼3D行业产业运行态势及投资规划深度研究报告
- 检修安全监护管理制度
- 产科工作管理制度
- 初中历史教师业务考试试题及答案
- 导尿管相关尿路感染预防与控制试题(附答案)
- 中医烧伤课件
- 2025-2030中国水下混凝土行业市场发展趋势与前景展望战略研究报告
- GB/T 30134-2025冷库管理规范
- 2025年心理咨询师基础理论知识测试卷:心理咨询心理学理论体系试题
评论
0/150
提交评论