维护就是在软件交付使用后进行的修改,修改之前必须理解待修改的对象,修改之后应该进行必要的测试,以保证所做的修改是正确的。如果是改正性维护,还必须预先进行调试以确定错误的具体位置。因此,决定软件可维护性的因素主要有下述5个。
1.可理解性
软件可理解性表现为外来读者理解软件的结构、功能、接口和内部处理过程的难易程度。模块化(模块结构良好,高内聚,松耦合)、详细的设计文档、结构化设计、程序内部的文档和良好的高级程序设计语言等等,都对提高软件的可理解性有重要贡献。
2.可测试性
诊断和测试的容易程度取决于软件容易理解的程度。良好的文档对诊断和测试是至关重要的,此外,软件结构、可用的测试工具和调试工具,以及以前设计的测试过程也都是非常重要的。维护人员应该能够得到在开发阶段用过的测试方案,以便进行回归测试。在设计阶段应该尽力把软件设计成容易测试和容易诊断的。
对于程序模块来说,可以用程序复杂度来度量它的可测试性。模块的环形复杂度越大,可执行的路径就越多,因此,全面测试它的难度就越高。
3.可修改性
软件容易修改的程度和本书第4章讲过的设计原理和启发规则直接有关。耦合、内聚、信息隐藏、局部化、控制域与作用域的关系等等,都影响软件的可修改性。
4.可移植性
软件可移植性指的是把程序从一种计算环境(硬件配置和操作系统)转移到另一种计算环境的难易程度。把与硬件、操作系统以及其他外部设备有关的程序代码集中放到特定的程序模块中,可以把因环境变化而必须修改的程序局限在少数程序模块中,从而降低修改的难度。
5.可重用性
所谓重用是指同一事物不做修改或稍加改动就在不同环境中多次重复使用。大量使用可重用的软件构件来开发软件,可以从下述两个方面提高软件的可维护性。
(1)通常,可重用的软件构件在开发时经过很严格的测试,可靠性比较高,且在每次重用过程中都会发现并清除一些错误,随着时间推移,这样的构件将变成实质上无错误的。因此,软件中使用的可重用构件越多,软件的可靠性越高,改正性维护需求越少。
(2)很容易修改可重用的软件构件使之再次应用在新环境中,因此,软件中使用的可重用构件越多,适应性和完善性维护也就越容易。
文档是影响软件可维护性的决定因素。由于长期使用的大型软件系统在使用过程中必然会经受多次修改,所以文档比程序代码更重要。
软件系统的文档可以分为用户文档和系统文档两类。用户文档主要描述系统功能和使用方法,并不关心这些功能是怎样实现的;系统文档描述系统设计实现和测试等各方面的内容。
总的说来,软件文档应该满足下述要求。
(1)必须描述如何使用这个系统,没有这种描述时即使是最简单的系统也无法使用。
(2)必须描述怎样安装和管理这个系统。
(3)必须描述系统需求和设计。
(4)必须描述系统的实现和测试,以便使系统成为可维护的。
下面分别讨论用户文档和系统文档。
1.用户文档
用户文档是用户了解系统的第一步,它应该能使用户获得对系统的准确的初步印象。文档的结构方式应该使用户能够方便地根据需要阅读有关的内容。
用户文档至少应该包括下述5方面的内容。
(1)功能描述,说明系统能做什么。
(2)安装文档,说明怎样安装这个系统以及怎样使系统适应特定的硬件配置。
(3)使用手册,简要说明如何着手使用这个系统(应该通过丰富例子说明怎样使用常用的系统功能,还应该说明用户操作错误时怎样恢复和重新启动)。
(4)参考手册,详尽描述用户可以使用的所有系统设施以及它们的使用方法,还应该解释系统可能产生的各种出错信息的含义(对参考手册最主要的要求是完整,因此通常使用形式化的描述技术)。
(5)操作员指南(如果需要有系统操作员的话),说明操作员应该如何处理使用中出现的各种情况。
上述内容可以分别作为独立的文档,也可以作为一个文档的不同分册,具体做法应该由系统规模决定。
2.系统文档
所谓系统文档指从问题定义、需求说明到验收测试计划这样一系列和系统实现有关的文档。描述系统设计、实现和测试的文档对于理解程序和维护程序来说是极端重要的。和用户文档类似,系统文档的结构也应该能把读者从对系统概貌的了解,引导到对系统每个方面每个特点的更形式化更具体的认识。本书前面各章已经较详细地介绍了各个阶段应该产生的文档,此处不再重复。
可维护性是所有软件都应该具备的基本特点,必须在开发阶段保证软件具有7.4.1节中提到的那些可维护因素。在软件工程过程的每一个阶段都应该考虑并努力提高软件的可维护性,在每个阶段结束前的技术审查和管理复审中,应该着重对可维护性进行复审。
在需求分析阶段的复审过程中,应该对将来要改进的部分和可能会修改的部分加以注意并指明;应该讨论软件的可移植性问题,并且考虑可能影响软件维护的系统界面。
在正式的和非正式的设计复审期间,应该从容易修改、模块化和功能独立的目标出发,评价软件的结构和过程;设计中应该对将来可能修改的部分预作准备。
代码复审应该强调编码风格和内部说明文档这两个影响可维护性的因素。
在设计和编码过程中应该尽最使用可重用的软件构件,如果需要开发新的构件,也应该注意提高构件的可重用性。
每个测试步骤都可以暗示在软件正式交付使用前,程序中可能需要做预防性维护的部分。在测试结束时进行最正式的可维护性复审,这个复审称为配置复审。配置复审的目的是保证软件配置的所有成分是完整的、一致的和可理解的,而且为了便于修改和管理已经编目归档了。
在完成了每项维护工作之后,都应该对软件维护本身进行仔细认真的复审。
维护应该针对整个软件配置,不应该只修改源程序代码。当对源程序代码的修改没有反映在设计文档或用户手册中时,就会产生严重的后果。
每当对数据、软件结构、模块过程或任何其他有关的软件特点做了改动时,必须立即修改相应的技术文档。不能准确反映软件当前状态的设计文档可能比完全没有文档更坏。在以后的维护工作中很可能因文档不完全符合实际而不能正确理解软件,从而在维护中引入过多的错误。
用户通常根据描述软件特点和使用方法的用户文档来使用、评价软件。如果对软件的可执行部分的修改没有及时反映在用户文档中,则必然会使用户因为受挫折而产生不满。
如果在软件再次交付使用之前,对软件配置进行严格的复审,则可大大减少文档的问题。事实上,某些维护要求可能并不需要修改软件设计或源程序代码,只是表明用户文档不清楚或不准确,因此只需要对文档做必要的维护。
几乎所有历史比较悠久的软件开发组织,都有一些十几年前开发出的“老”程序。目前,某些老程序仍然在为用户服务,但是,当初开发这些程序时并没有使用软件工程方法学来指导。因此,这些程序的体系结构和数据结构都很差,文档不全甚至完全没有文档,对曾经做过的修改也没有完整的记录。
怎样满足用户对上述这类老程序的维护要求呢?为了修改这类程序以适应用户新的或变更的需求,有以下几种做法可供选择。
(1)反复多次地做修改程序的尝试,与不可见的设计发源代码“顽强战斗”,以实现所要求的修改。
(2)通过仔细分析程序尽可能多地掌握程序的内部工作细节,以便更有效地修改它。
(3)在深入理解原有设计的基础上,用软件工程方法重新设计、重新编码和测试那些需要变更的软件部分。
(4)以软件工程方法学为指导,对程序全部重新设计、重新编码和测试,为此可以使用CASE工具(逆向工程和再工程工具)来帮助理解原有的设计。
第一种做法很盲目,通常人们采用后3种做法。其中第4种做法称为软件再工程,这样的维护活动也就是本章7.1节中所说的预防性维护,而第3种做法实质上是局部的再工程。
预防性维护方法是由Miller提出来的,他把这种方法定义为:“把今天的方法学应用到昨天的系统上,以支持明天的需求。”
粗看起来,在一个正在工作的程序版本已经存在的情况下重新开发一个大型程序,似乎是一种浪费。其实不然,下述事实很能说明问题。
(1)维护一行源代码的代价可能是最初开发该行源代码代价的14-40倍。
(2)重新设计软件体系结构(程序及数据结构)时使用了现代设计概念,它对将来的维护可能有很大的帮助。
(3)由于现有的程序版本可作为软件原型使用,开发生产率可大大高于平均水平。
(4)用户具有较多使用该软件的经验,因此,能够很容易地搞清新的变更需求和变更的范围。
(5)利用逆向工程和再工程的工具,可以使一部分工作自动化。
(6)在完成预防性维护的过程中可以建立起完整的软件阶段。
虽然由于条件所限,目前预防性维护在全部维护活动中仅占很小比例,但是,我们不应该忽视这类维护,在条件具备时应该主动地进行预防性维护。