第十二章面向对象实现程序设计语言程序设计风格面向对象测试面向对象设计的结果既可以用面向对象语言、也可以用非面向对象语言实现。选择编程语言的关键因素,是语言的一致的表达能力、可重用性及可维护性。从面向对象观点看来,能够更完整、更准确地表达问题域语义的面向对象语言的语法是非常重要的,因为这会带来下述几个重要优点:程序设计语言优点1.面向对象语言与非面向对象语言1)一致的表示方法2)可重用性3)可维护性因此,在选择编程语言时,应该考虑的首要因素,是在供选择的语言中哪个语言能最好地表达问题域语义。一般说来,应该尽量选用面向对象语言来实现面向对象分析、设计的结果。面向对象语言的形成借鉴了历史上许多程序语言的特点,从中吸取了丰富的营养。当今的面向对象语言,从20世纪50年代诞生的LISP语言中引进了动态联编的概念和交互式开发环境的思想,从20世纪60年代推出的SIMULA语言中引进了类的概念和继承机制,此外,还受到20世纪70年代末期开发的Modula_2语言和Ada语言中数据抽象机制的影响。在选择面向对象语言时应该着重考察的一些技术特点:2.面向对象语言的技术特点1)支持类与对象概念的机制所有面向对象语言都允许用户动态创建对象,并且可以用指针引用动态创建的对象。两种管理内存的方法,一种是由语言的运行机制自动管理内存,即提供自动回收“垃圾”的机制;另一种是由程序员编写释放内存的代码。2)实现整体-部分(即聚集)结构的机制两种实现方法,分别使用指针和独立的关联对象实现整体-部分结构。大多数现有的面向对象语言并不显式支持独立的关联对象,在这种情况下,使用指针是最容易的实现方法,通过增加内部指针可以方便地实现关联。3)实现一般-特殊(即泛化)结构的机制既包括实现继承的机制也包括解决名字冲突的机制。所谓解决名字冲突,指的是处理在多个基类中可能出现的重名问题,这个问题仅在支持多重继承的语言中才会遇到。某些语言拒绝接受有名字冲突的程序,另一些语言提供了解决冲突的协议。不论使用何种语言,程序员都应该尽力避免出现名字冲突。4)实现属性和服务的机制对于实现属性的机制应该着重考虑以下几个方面:支持实例连接的机制;属性的可见性控制;对属性值的约束。对于服务来说,主要应该考虑下列因素:支持消息连接(即表达对象交互关系)的机制;控制服务可见性的机制;动态联编。所谓动态联编,是指应用系统在运行过程中,当需要执行一个特定服务的时候,选择(或联编)实现该服务的适当算法的能力。动态联编机制使得程序员在向对象发送消息时拥有较大自由,在发送消息前,无须知道接受消息的对象当时属于哪个类。5)类型检查程序设计语言可以按照编译时进行类型检查的严格程度来分类。如果语言仅要求每个变量或属性隶属于一个对象,则是弱类型的;如果语法规定每个变量或属性必须准确地属于某个特定的类,则这样的语言是强类型的。强类型语言主要有两个优点:一是有利于在编译时发现程序错误,二是增加了优化的可能性。通常使用强类型编译型语言开发软件产品,使用弱类型解释型语言快速开发原型。6)类库大多数面向对象语言都提供一个实用的类库。某些语言本身并没有规定提供什么样的类库,而是由实现这种语言的编译系统自行提供类库。存在类库,许多软构件就不必由程序员重头编写了,这为实现软件重用带来很大方便。类库中往往包含实现通用数据结构的类,通常把这些类称为包容类。在类库中还可以找到实现各种关联的类。更完整的类库通常还提供独立于具体设备的接口类(例如,输入输出流),此外,用于实现窗口系统的用户界面类也非常有用,它们构成一个相对独立的图形库。7)效率使用拥有完整类库的面向对象语言,有时能比使用非面向对象语言得到运行更快的代码。这是因为类库中提供了更高效的算法和更好的数据结构。8)持久保存对象任何应用程序都对数据进行处理,如果希望数据能够不依赖于程序执行的生命期而长时间保存下来,则需要提供某种保存数据的方法。希望长期保存数据主要出于以下两个原因:(1)为实现在不同程序之间传递数据,需要保存数据;(2)为恢复被中断了的程序的运行,首先需要保存数据。9)参数化类所谓参数化类,就是使用一个或多个类型去参数化一个类的机制,有了这种机制,程序员就可以先定义一个参数化的类模板(即在类定义中包含以参数形式出现的一个或多个类型),然后把数据类型作为参数传递进来,从而把这个类模板应用在不同的应用程序中,或用在同一应用程序的不同部分。Eiffel语言中就有参数化类,C++语言也提供了类模板。10)开发环境软件工具和软件工程环境对软件生产率有很大影响。由于面向对象程序中继承关系和动态联编等引入的特殊复杂性,面向对象语言所提供的软件工具或开发环境就显得尤其重要了。至少应该包括下列一些最基本的软件工具:编辑程序,编译程序或解释程序,浏览工具,调试器(debugger)等。开发人员在选择面向对象语言时,还应该着重考虑以下一些实际因素。1)将来能否占主导地位为使自己的产品在若干年后仍然具有很强的生命力,人们可能希望采用将来占主导地位的语言编程。但最终决定选用哪种面向对象语言的实际因素,往往是诸如成本之类的经济因素而不是技术因素。2)可重用性采用面向对象方法开发软件的基本目的和主要优点,是通过重用提高软件生产率。因此,应优先选用能够最完整、最准确地表达问题域语义的面向对象语言。3.选择面向对象语言3)类库和开发环境决定可重用性的因素,不仅仅是面向对象程序语言本身,开发环境和类库也是非常重要的因素。事实上,语言、开发环境和类库这3个因素综合起来,共同决定了可重用性。4)其他因素在选择编程语言时,应该考虑的其他因素还有:对用户学习面向对象分析、设计和编码技术所能提供的培训服务;在使用这个面向对象语言期间能提供的技术支持;能提供给开发人员使用的开发工具、开发平台、发行平台;对机器性能和内存的需求;集成已有软件的容易程度等。良好的程序设计风格对保证程序质量的重要性。良好的程序设计风格对面向对象实现来说尤其重要,不仅能明显减少维护或扩充的开销,而且有助于在新项目中重用已有的程序代码。1.提高可重用性一般说来,代码重用有两种:一种是本项目内的代码重用,另一种是新项目重用旧项目的代码。有助于实现这两类重用的程序设计主要有:1)提高方法的内聚2)减小方法的规模3)保持方法的一致性程序设计风格4)把策略与实现分开5)全面覆盖6)尽量不使用全局信息7)利用继承机制在面向对象程序中,使用继承机制是实现共享和提高重用程度的主要途径。(1)调用子过程。最简单的做法是把公共的代码分离出来,构成一个被其他方法调用的公用方法通过调用公用方法实现代码重用的示例如下图所示:(2)分解因子。通过因子分解实现代码重用的示例如下图所示:(3)使用委托。继承关系的存在意味着子类“即是”父类,因此,父类的所有方法和属性应该都适用于子类。(4)把代码封装在类中。提高可重用性的准则,也能提高程序的可扩充性。此外,下列的面向对象程序设计准则也有助于提高可扩充性:1)封装实现策略2)不要用一个方法遍历多条关联链3)避免使用多分支语句4)精心确定公有方法2.提高可扩充性程序员在编写实现方法的代码时,既应该考虑效率,也应该考虑健壮性。通常需要在健壮性与效率之间做出适当的折衷。为提高健壮性应该遵守以下几条准则:1)预防用户的操作错误2)检查参数的合法性3)不要预先确定限制条件4)先测试后优化3.提高健壮性1.测试策略1)面向对象的单元测试2)面向对象的集成测试面向对象软件的集成测试主要有下述两种不同的策略:(1)基于线程的测试(threadbasedtesting)(2)基于使用的测试(usebasedtesting)3)面向对象的确认测试传统的黑盒测试方法也可用于设计确认测试用例,但是,对于面向对象的软件来说,主要还是根据动态模型和描述系统行为的脚本来设计确认测试用例。面向对象测试1)测试类的方法测试单个类的方法主要有随机测试、划分测试和基于故障的测试等3种:(1)随机测试(2)划分测试1)基于状态的划分2)基于属性的划分3)基于功能的划分(3)基于故障的测试2)集成测试方法(1)多类测试(2)从动态模型导出测试用例2.设计测试用例