MIT OpenCourseWare
OCW Home Course List About OCW Help with OCW Feedback
搜索
全部课程
Search
» 高级搜索
 课程主页
 教学大纲
 教学日程
 参考读物
 讲义
 复习
 实验
 作业
 考试
 项目
 工具
 相关资源

讲义 S7:为软件系统建立文档


目标

一个没有良好文档的系统就算能够运行也没有什么价值。对于小型的、不重要的、只使用很短时间的程序,一点注释就够了。但是对于大部分程序,如果唯一的文档就是代码本身,那么这个程序将很快过时而且难以维护。大多数初学者通常对于在建档方面做出的小小努力就能够获得回报、甚至对影响整个项目感到惊讶。

除非你是永不犯错的,并且生活在一个从不改变的世界中,否则你必然会重新面对你以前所编写的代码,并对你在以前开发中所做的选择感到疑惑。如果你不对你的选择建立文档,你很可能会犯和以前开发过程中同样的错误,而这些错误很容易就能说明白。缺乏文档不仅会增加额外的工作,而且会损坏代码的质量。例如,假设你对问题没有一个清晰的描述,那么你就很难开发一个明白的解决方案。

学习如何进行软件系统建档是困难的,而且它要求成熟的基于项目的判断能力。建立文档过少是一个常见的错误,但是如果走向另一个极端也同样会产生问题:如果你的文档太多,那些文档将成为阅读者巨大的障碍,而且它的维护工作将是一个负担。只对正确的事物建模是至关重要的。如果文档的长度令读者望而生怯,那么它就没有什么价值。

初学者常常会被简单的问题所吸引而在上面作出大量的努力,因此比较容易建立文档。但是那只能浪费时间,你从你所做的努力中并不能学到什么,最终得到没有什么用处的文档。初学者还经常不愿意为问题建档,这样做是目光短浅的:如果你知道你所设计的一些方面并不完善、问题的某些部分并没有说清楚、或者代码中可能存在一些错误,你就会同意我们的观点了。如果认识到以上问题,你就能够节省读者花在一些令人迷惑的看起来是错误的问题上的时间,在你遇到问题的时候能够记得该查看什么地方,最终得到一个更加真实的有用的文档。

另一个问题是在什么时候建立文档。虽然有时将建立文档的工作推迟到实验之后是有意义的,但是有经验的开发者一般会在系统开始时建立文档,甚至对于临时代码、初始问题分析、以及设计草图也会建立文档,因为他们发现这样做可以在实验中得到更多有用的结果。此外,因为他们养成了建立文档的习惯,对任何问题建立文档就是自然而然的事情了。

本讲义将针对6.170项目的建档问题给你一些指导。文中给出了一个概要结构以及一些要求做到的要素,但是对于细节问题,文中留出了很大的空间给你自己做决定。重要的是不要把建立文档当成一件无关紧要的、无聊的事情;如果你这么认为,由你所编写的文档必定会是没有什么用处的、难以阅读的。所以,请有意识的编写文档:在编写文档的时候问你自己为什么要那样做,你的时间是否达到了最高的使用效率。

你可以随意的将我们提供的讲义粘贴到你的文档中,特别地,你可能会需要使用部分的问题定义讲义来描述你的需求。但是,请记得将你所做的任何改变清楚的标明出来,这样你的助教就不用手动去模拟Unix diff

大纲

你的文档应该有以下结构。一个典型的6.170课程的大概页数已经给出,但是这些只是指导而不是要求。

1. 需求

需求部分描述要解决的问题本身以及它的解决方案。这部分的文档是开发者和使用者都需要关注的,它应该包含一些特殊实现策略的细节。系统其它部分的文档可能对于使用者并没有什么作用,而对开发者、维护者则是必须的。

  1. 概述(大约一页). 一个对于系统目标以及系统所能提供功能的解释。
  2. 修订的规格说明. 如果你已经得到了系统行为的详细规格说明,你必然会发现系统的某些部分并没有说明或者没有说清楚。在这个阶段你应该把你对于需求的任何想法以及你对需求所做的任何改动说清楚。
  3. 用户手册(1-5页). 对于用户该怎样使用系统的详细描述:用户可以进行什么操作、命令行的参数都有哪些、等等。格式的详细说明应该放在附录中。任何假定的外部环境都应该在这里说清楚:例如,这个程序是否只能在特定平台上运行、假设已经存在了一个相应的目录结构、假设已经安装了相应的其他应用程序、等等。同概述一起,这个手册应该为用户提供关于系统的所有信息。
  4. 性能(半页). 系统正常运行需要哪些资源,它将要耗费多少时间和空间?
  5. 问题分析(2-10页). 一个对于以下问题的清楚描述。这包括在设计之后的一个概念模型(以及可能的用户接口),如果它们还没有被讨论到。问题分析通常包括一个或多个问题对象模型,它们的设置和关系的定义,以及任何关于巧妙解决方法的讨论。问题对象模型中的对象并非来自代码,而是来自问题域。对象模型应该包括图和所有关键的文本约束,它们应该安排的整洁易读。这部分还应该描述被否定掉了的解决方法,包括它们的原因、它们解决了的问题、或者没有完全说清楚将在稍后解决的方面。

你可能会发现使用用例将对你的修订的规格说明以及用户手册的编写大有帮助。一个用例是一个特殊的目标以及用户为达成此目标所做的一系列动作。一个客户可以检查动作列表,从而决定用户接口是否合理。如果用例集合覆盖了所有要求的用户目标,那么客户就有理由相信系统将能够达到它的目标。

2. 设计

你的文档的设计部分给出一个你的实现策略的高层视图。

  1. 概述(0.5-3页). 设计的一概述:顶层组织结构、特殊的设计方案、库和其它第三方模块的使用、以及没有确定的或可能改变的方面的指示。还应包括设计中的问题:可能产生错误的决定,以及在灵活性和高效性两者间可能不恰当的选择。
  2. 运行时结构(1-5页). 一个运行中程序状态结构的描述,以代码对象模型来表示。这个模型应该隐藏抽象数据类型的表示,它的目标是展现各个对象之间的关系。对象模型应该包括图和所有关键的文本约束,它们应该安排的整洁易读。应该解释特殊的、特别复杂的或者对于整个设计特别重要的数据类型的表示(同它们的抽象函数以及表示不变式(representation invariants或rep. invariants)一起)。注意,抽象函数和rep. invariants还应该出现在它们在代码中本来应该出现的位置。
  3. 模块结构(1-5页). 一个对于程序文本语法结构的描述,用模块依赖图来表示。这当中应该包括包结构,并展示Java™接口和类。并不需要描述Java™API类之间的依赖关系。你的MDD应该安排的整洁易读。解释为什么要选择这样的语法结构(例如,对于分解接口的介绍-它们分解了什么,以及为什么要这样做),以及特殊的设计模式是怎样使用的。

要解释分解或其他设计中的决定,从它们对程序简单性、可扩展性(方便添加新功能)、可拆分性(各个的团队成员可以同时进行设计的各个不同部分,而不用常常交流)或相似的软件工程目标来进行讨论。

3.测试

你的文档的测试部分表明了你为了检验和确认系统所做的工作。(对于一个真实的系统,这可能包括用户测试以确定系统是否满足了需求部分所描述的功能,还应包括通过运行多组测试来检验代码的正确性。)如同不能用代码甚至类的列举来代替你的系统的设计文档,你也不应该仅仅列举你所执行的测试来代替这部分文档。你应该讨论测试用例是怎样选择的、为什么选择它们就足够了、为什么一个阅读者应该相信没有遗漏重要的测试、以及为什么读者应该相信系统将会按照要求的那样执行操作。

  1. 策略(1-2页). 一个对于总体测试策略的解释:黑盒和/或白盒测试,自顶向下测试和/或自底向上测试,所使用的测试驱动的种类、测试的数据源、测试用例、覆盖规模、编译时检查或者运行时断言、你的代码的论证,等等。对程序的不同部分,你可能需要使用不同的技术(或者混和使用不同技术)。对于不同情况,调整你的选择。

    解释通过你所指定的策略你期望找到哪些错误。讨论设计的哪些方面使得它容易或难以确认。

  2. 测试结果(0.5-2页). 总结都进行了哪些测试,以及还有哪些没有进行:测试了哪些模块,有多彻底?指出代码的可信程度:彻底清楚了哪种错误?还可能存在哪种错误?

4.反思

文档中的反思(通常称为“事后剖析”)部分是你能够从开发中特殊部分的失败或成功中得到的对于以后的软件开发有用的规则。什么最令你吃惊?在开始的时候你最想知道什么?在开发时怎样避免你所遇到的问题?

  1. 评价(0.5-1页). 你认为什么开发中的成功或失败:没有解决的设计问题、性能问题,等等。指明你的设计中哪些功能是重要的。指出你在设计或实现中使用的令你感到骄傲的技术。讨论你在设计中犯了哪些错误,以及它们产生的原因。
  2. 教训(0.2-1页). 从开发中你学到了哪些教训:如果在来一次和这次将有什么不同,以及怎样纠正设计和实现中的错误。描述问题产生的原因,例如错过了里程碑或者已知的错误和限制。
  3. 已知的错误和限制:准确描述你的实现中没能满足规格说明的地方。虽然这样会因为错误和缺少功能丢分,但是你将会因为明确标明哪些错误以及问题的根源得到额外的分数。

5.附录

附录包含系统的低层细节,这些细节对于系统的高层理解不是必要的,但是需要通过它们来实践或检验文档中其他部分提出的主张。

  1. 格式. 一个对于程序中用到的所有格式的描述:对于文件I/O、命令行参数、用户对话框、网络交互的消息格式、等等。它们应该被分解为用户可见的格式和内部格式。用户可见格式指用户可以看到的需求和用户手册中的部分,内部格式指你的文档中的其他部分中的格式说明。
  2. 模块规格说明. 你应该从你的代码中提取规格说明并将它们分别表示在这里。如果你按照6.170 doclet的Javadoc风格为你的代码编写注释,你将能够从代码中自动生成规格说明。一个抽象类型的规格说明应该包括它的概述、规格说明域、以及抽象不变式(规格说明约束)。抽象函数和rep invariant不属于一个类型的规格说明。
  3. 试用例. 理想上,你的实验台从一个按照易于读写的格式保存测试用例的文件中读取测试。不需要包括特别大的测试用例;例如,你可能只关心为强度测试产生的随机输入的大小,以及提供产生测试的程序。指出各组测试的目的(例如,“强度测试,大输入”,“划分测试,对于整型参数,所有+/-/0的组合”)。

为代码建立文档

规格说明层次的注释

抽象数据类型. 每个抽象数据类型(类或者接口)应该有:

  1. 一个概述部分,使用一到两行解释这个类型表示什么对象以及它们是否是可变的。
  2. 一个规格说明域列表。可能只有一个;例如,一组可能有一个elems域表示元素的集合。每个域应该有一个名称、一个类型和一段简短的说明。定义一个派生域可能会使得方法规格说明的编写变得容易很多;对于每个派生域,你应该指出它是派生的以及说明它是如何从其它域中派生出来的。可能会有约束规格说明域可能取值的规格说明不变式,如果有的话,你应该给出它们的说明。

方法规格说明. 类中所有的公有方法都应该有规格说明,有技巧的私有方法也应该说明。方法规格说明遵守规格说明讲义中描述需求、改变、抛出、效果、返回的结构。注意,在6.170课程中如果没有特殊说明,你可以认为所有方法的参数都不能为空。

实现级的注释

执行程序注释. 类的注释应该包括以下要素(对于重要的类,还应包括设计文档中的运行时结构部分):

  1. 一个抽象函数,它使用表示域定义各个规格说明域。只有是抽象数据类型的类才要求抽象函数,对于异常或GUI widgets这样的类并不要求。
  2. 一个表示不变式(representation invariant). 任何有表示的类都需要RIs。如果可以执行,我们强烈推荐你使用checkRep()来测试你的不变式。注意你的不变式假设中哪些可以为空,哪些不能。
  3. 对于表示很复杂的类,一段注解用来解释表示的选择( 也称为表示原理):做出了怎样的折中,什么方案在提出后又被否决了(为什么)。

运行时断言。这些应该按照课程中介绍的那样谨慎的使用。要了解关于如何使用运行时断言来改进代码质量,请查看Writing Solid Code by Steve Maguire, Microsoft Press, 1995。

注释。你应该认真的对你的代码进行良好的注释。关于注释格式的指导,请查阅Java设计指南讲义。要了解在更一般的编程中关于注释的更出色的讨论和更好的建义,请查看The Practice of Programming by Brian W. Kernighan and Rob Pike, Addison-Wesley, Inc., 1999。

返回顶部




 
MIT Home
Massachusetts Institute of Technology Terms of Use Privacy