- 87 ------------------------ Page 100-----------------------目经理认为自己可以独立解决问题,他就不会告诉老板。有两种掀开毯子把污垢展现在老板面前的方法,它们必须都被采用。一种是减少角色冲突和鼓励状态共享,另一种是猛地拉开地毯。减少角色的冲突。首先老板必须区别行动信息和状态信息。他必须规范自己,不对项目经理可以解决的问题做出反应,并且决不在检查状态报告的时候做安排。我曾经认识一个老板,他总是在状态报告的第一个段落结束之前,拿起电话发号施令。这样的反应肯定压制信息的完全公开。不过,当项目经理了解到老板收到项目报告之后不会惊慌,或者不会越俎代庖时,他就逐渐会提交真实的评估结果。如果老板把会见、评审、会议明显标记为状态检查(status-meeting)和问题-行动(problem-action)会议,并且相应控制自己的行为,这对整个过程会很有帮助。当然,事态发展到无法控制时,状态检查会议会演变成问题-行动会议。不过,至少每个人知道“当时游戏的分数是多少”,老板在接过“皮球”之前也会三思。猛地拉开地毯。不论协作与否,拥有能了解状态真相的评审机制是必要的。PERT 图以及频繁的里程碑是这种评审的基础。大型项目中,可能需要每周对某些部分进行评审,大约一个月左右进行整体评审。有报告显示关键的文档是里程碑和实际的完成情况。图14.1是上述报告中的一段摘录。它显示了一些问题:手册(SLR)的批准时间有所冲突,其中一个的时间比独立产品测试(Alpha)的开始时间还要迟。这样一份报告将作为2 月1号会议的议程,使得每个人都知道问题的所在,而产品构件经理应准备解释延迟的原因,什么时候结束,采取的步骤和需要的任何帮助——老板提供的,或者是其他小组间接提供的。- 88 ------------------------ Page 101-----------------------注:SYSTEM/360 SUMMARY STATUS REPORT -SYSTEM/360 总结状态报告- 89 ------------------------ Page 102-----------------------OS/360 LANGUAGE PROCESSORS + SERVICE PROGAMS-OS/360 语言处理器 + 服务程序AS OF FEBRAURAY 01.1965-1965 年2 月1 号APPOVEL-批准COMPLETED-完成PROJECT-项目LOCATION-地点COMMITMNT ANNOUNCE RELEASE-计划发布OBJECTIVE AVAILABLE APPROVED-目标制订批准SPECS AVAILABLE APPROVED-规格说明提交批准SRL AVAILABLE APPROVED-SRL 提交批准ALPHA TEST ENTRY EXIT -ALPHA 测试进入 退出COMP TEST START COMPLETE-单元测试开始 结束SYS TEST START COMPLETE-系统测试开始 结束BULLETIN AVAILABLE APPROVED-公告发布批准BETA TEST ENTRY EXIT -BETA 测试进入 退出图14.1Bell 实验室的V. Vyssotsky 添加了以下的观察意见:我发现在里程碑报告中很容易记录“计划”和“估计”的日期。计划日期是项目经理的工作产物,代表了经协调后的项目整体工作计划,它是合理计划之前的判断。估计日期是最基层经理的工作产物,基层经理对所讨论的工作有着深刻的了解,估计日期代表了在现有资源和已得到了作为先决条件的必要输入(或得到了相应的承诺)的情况下,基层经理对实际实现日期的最佳判断。项目经理必须停止对这些日期的怀疑,而将重点放在使其更加精确上、以便得到没有偏见的估计,而不是那些合乎心意的乐观估计或者自我保护的保守估计。一旦它们在每个人的脑海中形成了清晰的印象,项目经理就可以预见到将来哪些地方如果他4不采取任何措施,就会出现问题 。PERT 图的准备工作是老板和要向他进行汇报的经理们的职责。需要一个小组(一至三个人)来关注它的更新、修订和报告,这个小组可以看作是老板的延伸。对大型项目,这种计划和控制(Plan and Control)小组的价值是非常可贵的。小组的职权仅限于向产品线经理询问他们什么时候设定或更改里程碑,以及里程碑是否被达到。计划和控制小组处理所有的文字工作,因此产品线经理的负担将会减到最少——仅仅需要作出决策。我们拥有一个富有热情的、有经验的、熟练的计划和控制小组。这个小组由A. M.Pietrasanta 负责,他投入了大量创造天分来设计有效的、谦逊的控制方法。结果,我发现他的小组被广为尊重,而不仅仅是被容忍。对于这样一个本来就十分敏感的角色而言,这的- 90 ------------------------ Page 103-----------------------确是一个成功。对计划和控制职能进行适度的技术人力投资是非常值得赞赏的。它对项目的贡献方式和直接开发软件产品有很大的不同。计划和控制小组作为监督人员,明白地指出了不易察觉的延迟,并强调关键的因素。他们是早期预警系统,防止项目以一次一天的方式落后一年。- 91 ------------------------ Page 104-----------------------另外一面(The other face)不了解,就无法真正拥有。- 歌德- 克雷布What we do not understand we do not possess.- GOETHEO give me commentators plain, Who with no deep researches vex the brain[jypan1]- CRABBE计算机程序是从人传递到机器的一些信息。为了将人的意图清晰地传达给不会说话的机器,程序采用了严格的语法和严谨的定义。但是书面的计算机程序还有其他的呈现面貌:向用户诉说自己的“故事”。即使是完全开发给自己使用的程序,这种沟通仍然是必要的。因为记忆衰退的规律会使用户-作者失去对程序的了解,于是他不得不重拾自己劳动的各个细节。公共应用程序的用户在时间和空间上都远离它们的作者,因此对这类程序,文档的重要性更是不言而喻!对软件编程产品来说,程序向用户所呈现的面貌和提供给机器识别的内容同样重要。面对那些文档“简约”的程序,我们中的大多数人都不免曾经暗骂那些远在他方的匿名作者。因此,一些人试图向新人慢慢地灌输文档的重要性:旨在延长软件的生命期、克服惰性和进度的压力。但是,很多次尝试都失败了,我想很可能是由于我们使用了错误的方法。Thomas J. Watson 讲述了他年轻时在纽约北部,刚开始做收银机推销员的经历。他带- 92 ------------------------ Page 105-----------------------着一马车的收银机,满怀热情地动身了。他工作得非常勤奋,但是连一台收银机也没有卖出去。他很沮丧地向经理汇报了情况,销售经理听了一会儿,说道:“帮我抬一些机器到马车上,收紧缰绳,出发!”他们成功了。在接下来的客户拜访过程中,经理身体力行地演示了如何出售收银机。事实证明,这个方法是可行的。我曾经非常勤奋地给我的软件工程师们举办了多年关于文档必要性以及优秀文档所应具备特点方面的讲座,向他们讲述——甚至是热诚地向他们劝诫以上的观点。不过,这些都行不通。我想他们知道如何正确地编写文档,却缺乏工作的热情。后来,我尝试了向马车上搬一些收银机,以此演示如何完成这项工作。结果显示,这种方法的效果要好得多。所以,文章剩余部分将对那些说教之辞一笔带过,而把重点放在“如何做(才能产生一篇优秀的文档)上。需要什么样的文档不同用户需要不同级别的文档。某些用户仅仅偶尔使用程序,有些用户必须依赖程序,还有一些用户必须根据环境和目的的变动对程序进行修改。使用程序。每个用户都需要一段对程序进行描述的文字。可是大多数文档只提供了很少的总结性内容,无法达到用户要求,就像是描绘了树木,形容了树叶,但却没有一副森林的图案。为了得到一份有用的文字描述,就必须放慢脚步,稳妥地进行。1. 目的。主要的功能是什么?开发程序的原因是什么?2. 环境。程序运行在什么样的机器、硬件配置和操作系统上?3. 范围。输入的有效范围是什么?允许显示的合法范围是什么?4. 实现功能和使用的算法。精确地阐述它做了什么。5. 输入-输出格式。必须是确切和完整的。6. 操作指令。包括控制台及输出内容中正常和异常结束的行为。7. 选项。用户的功能选项有哪些?如何在选项之间进行挑选?8. 运行时间。在指定的配置下,解决特定规模问题所需要的时间?- 93 ------------------------ Page 106-----------------------9. 精度和校验。期望结果的精确程度?如何进行精度的检测?一般来说,三、四页纸常常就可以容纳以上所有的信息。不过往往需要特别注意的是表达的简洁和精确。由于它包含了和软件相关的基本决策,所以这份文档的绝大部分需要在程序编制之前书写。验证程序。除了程序的使用方法,还必须附带一些程序正确运行的证明,即测试用例。每一份发布的程序拷贝应该包括一些可以例行运行的小测试用例,为用户提供信心——他拥有了一份可信赖的拷贝,并且正确地安装到了机器上。然后,需要得到更加全面的测试用例,在程序修改之后,进行常规运行。这些用例可以根据输入数据的范围划分成三个部分。1. 针对遇到的大多数常规数据和程序主要功能进行测试的用例。它们是测试用例的主要组成部分。2. 数量相对较少的合法数据测试用例,对输入数据范围边界进行检查,确保最大可能值、最小可能值和其他有效特殊数据可以正常工作。3. 数量相对较少的非法数据测试用例,在边界外检查数据范围边界,确保无效的输入能有正确的数据诊断提示。修改程序。调整程序或者修复程序需要更多的信息。显然,这要求了解全部的细节,并且这些细节已经记录在注释良好的列表中。和一般用户一样,修改者迫切需要一份清晰明了的概述,不过这一次是关于系统的内部结构。那么这份概述的组成部分是什么呢?1. 流程图或子系统的结构图,对此以下有更详细的论述。2. 对所用算法的完整描述,或者是对文档中类似描述的引用。3. 对所有文件规划的解释。4. 数据流的概要描述——从磁盘或者磁带中,获取数据或程序处理的序列——以及在每个处理过程完成的操作。5. 初始设计中,对已预见修改的讨论;特性、功能回调的位置以及出口;原作者对可能会扩充的地方以及可能处理方案的一些意见。另外,对隐藏缺陷的观察也同样很有价值。- 94 ------------------------ Page 107-----------------------流程图流程图是被吹捧得最过分的一种程序文档。事实上,很多程序甚至不需要流程图,很少有程序需要一页纸以上的流程图。流程图显示了程序的流程判断结构,它仅仅是程序结构的一个方面。当流程图绘制在一张图上时,它能非常优雅地显示程序的判断流向,但当它被分成几张时,也就是说需要采用经过编号的出口和连接符来进行拼装时,整体结构的概观就严重地被破坏了。因此,一页纸的流程图,成为表达程序结构、阶段或步骤的一种非常基本的图示。同样,它也非常容易绘制。图15.1展示了一个子程序流程图的图样。图15.1:程序结构图(Courtesy of W. V. Wright)当然,上述图纸既没有,也不需要遵循精心制订的ANSI 流程图标准。所有图形元素如方框、连线、编号等,只需要能使这张详细的流程图可以理解就行了。因此,逐一记录的详细流程图过时而且令人生厌,它只适合启蒙初学者的算法思维。- 95 ------------------------ Page 108-----------------------1当Goldstine 和Neumann 引入这种方法时,框图和框图中的内容作为一种高级别语言,将难以理解的机器语言组合成一连串可理解的步骤。如同早期Iverson 所认识到的2,在系统化的高级语言中,分组已经完成,每一个方框相应地包含了一条语句(图15.2)。从而,方框本身变成了一件单调乏味的重复练习,可以去掉它们。这时,剩下的只有箭头。而连接相邻后续语句的箭头也是冗余的,可以擦掉它们。现在,留下的只有GOTO 跳转。如果大家遵守良好的规则,使用块结构来消除GO TO 语句,那么所有的箭头都消失了,尽管这些箭头能在很大程度上帮助理解。大家完全可以丢掉流程图,使用文字列表来表达这些内容。现实中,流程图被鼓吹的程度远大于它们的实际作用。我从来没有看到过一个有经验的编程人员,在开始编写程序之前,会例行公事地绘制详尽的流程图。在一些要求流程图的组织中,流程图总是事后才补上。一些公司则很自豪地使用工具软件,从代码中生成这个“不可缺少的设计工具”。我认为这种普遍经验并不是令人尴尬和惋惜的对良好实践的偏离(似乎大家只能对它露出窘迫的微笑),相反,它是对技术的良好评判,向我们传授了一些流程图用途方面的知识。耶稣门徒彼得谈到新的异教皈依者和犹太戒律时说道,“为什么让他们背负我们的祖先和我们自己都不能承担的重负呢?”(《使徒行传》 15:10 现代英文版本)。对于新的编程人员和陈旧的流程图方法,我持有相同的观点。自文档化(self-documenting)的程序数据处理的基本原理告诉我们,试图把信息放在不同的文件中,并努力维持它们之间的同步,是一种非常费力不讨好的事情。更合理的方法是:每个数据项包含两个文件都需要的所有信息,采用指定的键值来区别,并把它们组合到一个文件中。不过,我们在程序文档编制的实践中却违反了我们自己的原则。典型的,我们试图维护一份机器可读的程序,以及一系列包含记叙性文字和流程图的文档。结果和我们自己的认识相吻合。不同文件的数据保存带来了不良的后果。程序文档质量声名狼藉,文档的维护更是低劣:程序变动总是不能及时精确地反映在文档中。我认为相应的解决方案是“合并文件”,即把文档整合到源代码。这对正确维护是直接有力的推动,保证编程用户能方便、即时地得到文档资料。这种程序被称为自文档化- 96 ------------------------ Page 109-----------------------(self-documenting)。图15.2:流程图和对应程序的对比[节选自Thomas J.Cashman 和Willian J. Keys (Harper& Row,1971)所著的“Data Processing and Computer Programming: A Modular Approach”中的图15-41、15-44]现在看来,在程序中包括流程图显然是一种笨拙(但不是不可以)的做法。考虑到流程图方法的落后和高级语言的使用占统治地位,把程序和文档放在一起显然是很合理的。把源程序作为文档介质强制推行了一些约束。另一方面,对于文档读者而言,一行一- 97 ------------------------ Page 110-----------------------行的源程序本身就可以再次利用,使新技术的使用成为可能。现在,已经到了为程序文档设计一套彻底的新方法的时候了。文档是我们以及前人都不曾成功背负的重担。作为基本目标,我们必须试图把它的负