• 沒有找到結果。

Code Complete 代码大全

N/A
N/A
Protected

Academic year: 2022

Share "Code Complete 代码大全"

Copied!
542
0
0

加載中.... (立即查看全文)

全文

(1)

计算机实用软件技术系列丛书 软件开发人员必备工具书

Code Complete 代码大全

Steve McConnell 著 天奥 译 熊可宜 校

学苑出版社 1993

eVersion 1.01

缺陷报告: [email protected] 本书网站: www.delphidevelopers.com

(2)

(京)新登字 151 号

内容摘要

本书从软件质量和编程思想等方面论述了软件构造问题,并详细论述了紧跟潮流的新技术、

高屋建瓴的观点、通用的概念,还含有丰富而典型的程序示例。本书中所论述的技术不仅填补 了初级与高级编程技术之间的空白,而且也为程序员们提供了一个有关编程技巧的信息来源。

本书适合经验丰富、自学成才的程序员阅读,也适合于那些几乎不懂什么编程技巧的新程 序员阅读.

欲购本书的用户,请直接与北大 8721 信箱联系。邮编:100080,电话:2562329。

版权声明

本书中文版版权由 Microsoft 授予北京希望电脑公司和学苑出版社独家出版、发行。未经 出版者书面许可,本书的任何部分不得以任何形式或任何手段复制或传播。 Microsoft,MS,

Ms Dos 和 Windows 是微软公司注册商标。

计算实用软件技术系列丛书 软件开发人员必备工具书

代码大全

著者:Steve McConnell

翻译:天奥 审校:熊可宜 责任编辑:徐建军

出版发行:学苑出版社 邮政编码:100032 社址:北京市西城区成方街 33 号

印刷:兰空印刷厂 开本:787X1092 l/16

印张:33, 625 字数:777 千字 印数:1~10000 册

版次:1993 年 11 月第 1 版第 1 次 ISBN7—5077—0876—4/TP・ 25 本册定价;66.00

一——————————————————————————————————

学苑版图书 印、装错误可随时退换

(3)

前 言

近年来,关于软件开发的研究,进展是非常迅速的,但是开发实践却并非如此。许多程序 仍然是错误百出,充斥着过时的技术,从而无法满足用户需要。软件工业界和学术界的研究者 们,基本上已经解决了七十年代和八十年代在编程中遇到的问题,并发展了相应的技术。但是 直到现在,这些技术中的大部分仍然没有在软件编程中广泛采用,其主要原因是这些研究成果 主要发表在高度专业性的学术刊物中,普通的程序员们无心顾及。Sridhar Raghavan 和 Donald Chand(1989)的研究表明,一项新技术从诞生到被工业界广泛采用大约需要 5 到 15 年的时间。

本书的目的就是希望能够缩短新技术推广周期,使广大的程序员们可以迅速地获得软件开发的 最新方法与手段。

本书所面向的对象

本书中所收集的研究和编程经验,将有助于你编写出高质量的软件,并且使得开发周期缩 短。通过阅读本书,你将会对自己过去所犯过的错误有更深刻的理解,并懂得今后如何避免它 们。同时,书中所收集的丰富的编程经验也将使你在控制大规模项目和按要求对软件进行修改 和维护时感到得心应手。下面是适合阅读本书的几类人:

经验丰富的程序员

本书适合于想要得到一本全面易用的软件设计指南的那些资深程序员们阅读。由于本书的 中心内容是广大程序员们所熟知的实现过程,因此,无论是受过正规训练而已经验丰富的程序 员,还是完全靠自学成长起来的程序员,都能容易读懂本书所论述的先进技术和方法。

自学成才的程序员

本书尤其适合于很少受过正式专业训练的程序员阅读。1988 年有 100,000 人加入了程序 员大军,但其中只有 40,000 人是从计算机专业毕业的本科生,其余则几乎全是靠自学成才的。

同时,还有不计其数的其他各行各业的人员需要在工作中自己动手编一些程序。无论你所受到 的正规计算机专业训练多或少,本书都将使你对有效的编程方法和技巧有更深刻的理解。

学生

本书不仅适于实践经验丰富但理论基础薄弱的自学者阅读,同时也适于那些理论基础较好 但几乎不懂得什么编程诀窍的毕业生们阅读。新程序员们的某些实践经验来自于经验丰富的同 事.但主要还是靠自己──吃一堑,长一智──获得的,这往往是一个艰苦而缓慢的过程。通 过本书,可以使你在短时期内获得大量的经验和技巧,从而脱颖而出,所以,不妨一试。

本书的主要特点

完备的软件创建参考 本书从质量和编程思想等方面论述了软件构造问题。几乎囊括 了生成子程序、数据的输入输出与控制结构、调试、代码调整策略与技术等各方面的细节。在 使用本书时不必逐页阅读每一个细节,只要在需要时查阅你所感兴趣的章节即可。请把本书作

(4)

为手册而不是作为教科书来使用。

方便而实用的检查表 书中附有用于检查软件的结构设计、设计方法、模块和子程序等质 量的检查表,以供评估软件质量之用。同时,关于变量名、控制结构、方案布置、测试用例等 等检查表也将使你获益匪浅。

紧跟潮流的新技术 书中论述了许多目前最先进的技术,其中许多还只是刚刚投入应 用。由于本书取材于实践经验和最新研究成果两个方面,因此书中所提供的技术在相当长的时 间内都不会过时。

高屋建瓴的观点 阅读本书将使你跳出日常琐碎工作的圈子,对软件开发有一个总体上 的把握与认识。繁杂的日常工作往往使程序员们穷于应付而无暇阅读浩如烟海的书籍与资料,

本书丰富而翔实的第一手资料将弥补这一缺憾,使你对软件开发的策略作出正确决策而不致陷 入旷日持久的消耗战中。

通用的概念 无论你用的是 Pascal、C、C++、Ada、Basic、Fotran 还是 COBOL,都可 以从本书所论述的概念、方法和技巧中获得教益。

丰富而典型性的程序示例 书中含有大约 500 多个正反两方面的程序示例。之所以引入 这么多的示例,是因为笔者就是从各种例程中吸取了大部分的知识、经验与诀窍,因此笔者认 为最好的学习方法是多看例程。例程是用多种语言写成的,因为对于程序员来说,掌握多种语 言是其必不可少的基本素质之一。而且,只有掌握了不受语法规则限制的编程准则,才能真正 有效地提高你的编程效率和质量。 为了减轻由于使用多种语言所带来的额外负担,在例程中除 非确有必要,尽量避开了各个语言过于独特的部分。事实上,如果你真正注意每个例程所要说 明的问题的话,那么不必详细理解每个程序段,你也可以清楚地懂得程序的意义。同时,为了 进一步减轻读者的负担,对程序中有意义的部分作了标记。

本书的独特内容

本书关于创建活动的内容是从多个渠道获得的。有关创建活动的资料不仅分布得非常分 散,而且往往没有成文资料,事实上,卓有成效的优秀程序员们所使用的技术并不神秘,但由

于日常事务的繁重和工作任务的重压,程序员们很少有互相交流切磋的时间,因而,他们往往 缺乏有关编程技巧的有效信息来源。

本书中所论述的技术不仅填补了初级与高级编程课本之间的空白,而且也为程序员们提 供了一个有关编程技巧的信息来源。比如当你读过 C 语言初级教程之后,你可以再读 C 语言高

级教程,然后再去读 C 语言高级的高级教程,但读完这些书后,你还能再读什么书呢?你可以 再去读关于 PC、Macintosh 或 UNIX 等硬件或操作系统的书或者其它有关编程细节的书——因 为你如果不了解实现环境详情的话是无法充分有效地使用语言和程序的。但这只是讨论了编程 的一个方面,最有效的编程技术是那些不受实现环境及语言限制的技术。其它书往往忽略了这 一点,但这恰恰是本书的重点。

写作本书的目的

需要一本关于软件开发有效技术的书,是软件工程界所公认的。由计算机科学技术委员会 所发表的一份报告认为,提高程序质量和生产效率的最有效途径是出版一本关于软件开发有效

(5)

技术的书,而且这本书应该以手册的形式来组织。

同时,计算机编程技术的发展史也证明急需一本这方面的书,本书正是出于这个目的 才出版的。

创建活动未受到应有的重视

在一段时期内,软件开发与编码被当作是一回事,但随着软件开发周期中的其它活动被认 识,这一领域内的主要努力全部集中到了项目管理、需求分析、设计和测试等方面,创建活动 成了被遗忘的角落。

与这种现象相对应的思想是认为创建活动是软件开发中无关紧要的部分。于是,刚入门的 程序员被派去进行创建工作,为那些已经由上一阶段设计好的子程序编码。工作几年之后,他 可能会被提升至需求分析或项目管理部门。于是,这位程序员也会自豪地感到他不必再去编码 了。

创建活动是非常重要的

创建活动被忽视的另一个原因是:研究者和程序员们错误地认为与其它开发活动相比,创 建活动是一相对来说比较机械的活动,没有什么值得改进的。没有什么比这种想法离事实更远 了。

在小规模项目中,创建活动约占工作量的 80%,在中型项目中也要占 50%的工作量,而发 生在创建活动中的错误则占总错误的 50%到 75%。一项会产生 50%到 75%错误的工作是有许 多待改进之处的。

一些人认为,虽然创建时的错误占到总错误的 50%到 75%,但修改它们的费用与分析、设 计错误相比要少得多。的确,创建时的错误修改费用与前期工作错误修改费用相比是要少一些,

但是绝对数并不少。 Gerald Weinbers 曾在 1983 年报道过三个错误,每个错误的修改费用都高 达数百万美元,而每个错误都是一行编码层次上的代码错误。因此,绝不能以修改费用相对少 为理由来忽视创建活动。

具有讽刺意味的是,被忽视的创建活动事实上是唯一任何规模项目都必不可少的活动。

需求可以进行猜想而不必分析;结构可以被省略而不必设计。系统测试也可以不进行。但是,

如果你想有一个程序的话,你就不得不进行创建活动。

本书的独特性

如果创建活动的重要性是非常明显的话,那么本书恐怕就没有出版的必要了。但事实上几 乎没有什么书详细论述了这一主题。只有 15 年前出版过一本类似内容的书,讲述的是 ALGOL、

PL/I、Ratfor 等早已过时的语言中的具体问题。其它偶尔也有几本这方面的书,但却是教授们 针对教学用的演示性项目而写的,没有涉及到真正的工程问题。有些则偏激地推崇新技术而不 恰当地贬低了一些非常实用的成熟技术。总之,就内容的新颖、翔实、丰富和实用来看,目前 似乎还没有与本书相匹敌的关于创建活动的书。

编 者

(6)

目 录

第一章 欢迎进入软件创建世界………1

1.1 什么是软件创建.………1

1.2 软件创建的重要性.………3

1.3 小结.………4

第二章 利用隐喻对编程进行更深刻的理解………5

2.1 隐喻的重要性.. ….………5

2.2 如何使用软件隐喻.………6

2.3 通常的软件隐喻.………7

2.4 小结………11

第三章 软件创建的先决条件………12

3.1 先决条件重要性.………12

3.2 问题定义先决条件………16

3.3 需求分析先决条件………16

3.4 结构设计先决条件………20

3.5 选择编程语言先决条件.………26

3.6 编程约定.………29

3.7 应花在先决条件上的时间.………29

3.8 改变先决条件以适应你的项目.………30

3.9 小结. ………30

第四章 建立子程序步骤………31

4.1 建立程序步骤概述.………31

4.2 程序设计语言(PDL) ………31

4.3 设计子程序 ………33

4.4 子程序编码 ………37

4.5 检查子程序 ………42

4.6 小结 ………44

第五章 高质量子程序特点………45

5.1 生成子程序的原因 ………47

5.2 子程序名称恰当 ………51

5.3 强内聚性 ………52

5.4 松散耦合性 ………56

5.5 子程序长度 ………60

5.6 防错性编程 ………61

5.7 子程序参数 ………67

(7)

5.8 使用函数………71

5.9 宏子程序………72

5.10 小结………74

第六章 模块化设计 ………75

6.1 模块化:内聚性与耦合性………75

6.2 信息隐蔽………77

6.3 建立模块的理由………84

6.4 任何语言中实现模块………85

6.5 小结………90

第七章 高级结构设计 ………92

7.1 软件设计引论………92

7.2 结构化设计………95

7.3 面向对象………98

7.4 对目前流行设计方法的评论………102

7.5 往返设计………105

7.6 小结………109

第八章 生成数据 ………111

8.1 数据识别………111

8.2 自建数据类型的原因………113

8.3 自建类型的准则………115

8.4 使变量说明更容易………115

8.5 初始化数据的准则………120

8.6 小结………120

第九章 数据名称 ………121

9.1 选择名称………121

9.2 特定数据类型的命名………124

9.3 命名约定………128

9.4 非正式命名约定………129

9.5 匈牙利命名约定………132

9.6 短名称………136

9.7 要避免的名称………137

9.8 小结………139

第十章 变量 ………141

10.1 作用域………141

10.2 持久性………143

10.3 赋值时间………144

10.4 数据结构与控制结构的关系………145

10.5 变量功能单一性………146

10.6 全局变量………148

(8)

10.7 小结………153

第十一章 基本数据类型 ………154

11.1 常数………154

11.2 整型数………155

11.3 浮点数………157

11.4 字符和字符串………159

11.5 逻辑变量………161

11.6 枚举类型………162

11.7 命名常量………164

11.8 数组………166

11.9 指针………167

11.10 小结………175

第十二章 复杂数据类型 ………176

12.1 记录与结构………176

12.2 表驱动方法………179

12.3 抽象数据类型(ADTs)………192

12.4 小结………198

第十三章 顺序程序语句 ………199

13.1 必须有明确顺序的程序语句………199

13.2 与顺序无关的程序语句………201

13.3 小结………207

第十四章 条件语句 ………208

14.1 if 语句 ………208

14.2 case 语句………213

14.3 小结………216

第十五章 循环语句 ………217

15.1 选择循环类型………217

15.2 控制循环(Controlling The Loop)………222

15.3 编写循环的简单方法——从里到外………230

15.4 循环与数组的关系………232

15.5 小结………233

第十六章 少见的控制结构 ………234

16.1 goto 语句………234

16.2 return 语句………243

16.3 递归调用………244

16.4 小结………248

第十七章 常见的控制问题 ………249

17.1 布尔表达式………249

17.2 复合语句(块)………257

(9)

17.3 空语句………257

17.4 防止危险的深层嵌套………258

17.5 结构化编程的作用………264

17.6 用 goto 模拟结构化结构………267

17.7 控制结构和复杂性………269

17.8 小结………271

第十八章 布局和风格 ………272

18.1 基本原则………272

18.2 布局技巧………279

18.3 布局风格………280

18.4 控制结构布局………285

18.5 单条语句布局………292

18.6 注释布局………301

18.7 子程序布局………303

18.8 文件、模块和程序布局………306

18.9 小结………311

第十九章 文档 ………313

19.1 外部文档………313

19.2 编程风格作文档………314

19.3 注释还是不注释………316

19.4 有效注释的关键………318

19.5 注释方法………322

19.6 小结………337

第二十章 编程工具 ………338

20.1 设计工具………338

20.2 源代码工具………339

20.3 执行代码工具………343

20.4 面向工具的环境………345

20.5 建立自己的编程工具………346

20.6 理想编程环境………347

20.7 小结………350

第二十一章 项目大小如何影响创建 ………351

21.1 项目大小………351

21.2 项目大小时开发活动的影响………352

21.3 项目大小对错误的影响………356

21.4 项目大小对生产效率的影响………357

21.5 小结………358

第二十二章 创建管理 ………359

22. l 使用好的代码………359

(10)

22.2 配置管理………361

22.3 评估创建计划………364

22.4 度量………369

22.5 将程序员视为普通人………370

22.6 如何对待上司………374

22.7 小结………374

第二十三章 软件质量概述 ………375

23.1 软件质量特点………375

23.2 提高软件质量的方法………377

23.3 各种方法的效果………379

23.4 何时应作质量保证………381

23.5 软件质量的一般原则………381

23.6 小结………382

第二十四章 评审 ………384

24.1 评审在软件质量保证中的地位………384

24.2 检查………386

24.3 其它评审方法………389

24.4 小结………391

第二十五章 单元测试 ………393

25.1 单元测试在软件质量中的作用………393

25.2 单元测试的一般方法………395

25.3 测试技巧………396

25.4 典型错误………404

25.5 测试支持工具………408

25.6 提高测试质量………411

25.7 测试记录 ………412

25.8 小结………412

第二十六章 调试 ………414

26.1 概述………414

26 2 找错………417

26.3 修改错误………423

26.4 调协心理因素………425

26.5 调试工具………427

26.6 小结………430

第二十七章 系统集成 ………431

27.1 集成方法重要性………431

27.2 分段与递增集成………432

27.3 递增集成法………434

27.4 改进的公布法………439

27.5 小结………445

(11)

第二十八章 代码调整策略 ………446

28.1 功能综述………446

28.2 代码调整介绍………448

28.3 低效率情况………454

28.4 代码调整方法………457

28.5 小结………457

第二十九章 代码调试技术 ………459

29.1 循环………459

29.2 逻辑………460

29.3 数据转换………469

29.4 表达式………474

29.5 子程序………478

29.6 汇编语言再编码………484

29.7 调试技术快速参考………485

29.8 小结………486

第三十章 软件优化 ………487

30.1 软件优化种类………487

30.2 软件优化指南………488

30.3 编写新程序………489

30.4 小结………499

第三十一章 个人性格 ………501

31.1 个人性格是否和本书的主题无关………501

31.2 聪明和谦虚………502

31.3 好奇………503

31.4 诚实………504

31.5 交流和合作………506

31.6 创造力和合作………507

31.7 懒惰………507

31.8 不是你想象中那样起作用的性格………508

31.9 习惯………508

31.10 小结………510

第三十二章 软件开发方法的有关问题 ………511

32.1 克服复杂性………511

32.2 精选开发过程………513

32.3 首先为人编写程序,其次才是计算机………514

32.4 注重约定使用………515

32.5 根据问题范围编程………516

32.6 当心飞来之祸………517

(12)

32.7 重复………519

32.8 不要固执己见………520

32.9 小结………521

第三十三章 从何处获取更多的信息 ………522

33. l 软件领域的资料库………522

33.2 软件创建信息………523

33.3 创建之外的主题………523

33.4 期刊………524

33.5 参加专业组织………525

(13)

第一章 欢迎进入软件创建世界

目录

1.1 什么是软件创建(Software Construction)

1.2 软件创建的重要性 1.3 小结

相关章节

本书适合什么人阅读:见前言 阅读本书的益处:见前言 为什么要写这本书:见前言

大家都知道“Construction”这个词在一般情况下的意思是“建筑”。建筑工人盖房子、建 学校、造摩天大楼等时所进行的工作都是建筑。当你小的时候,你用积木进行“建筑工作”。因 此“Construction”指的是建造某个东西的过程。这个过程可能包括:计划、设计、检验等方面的 某些工作,但是,它主要是指在这其中的创造性工作。

1.1 什么是软件创建

开发计算机软件是一项非常复杂的工作,在过去的十五年中,研究者们指出了这项工作所 包括的主要方面,包括:

・ 问题定义

・ 需求分析

・ 实现计划

・ 总体设计

・ 详细设计

・ 创建即实现

・ 系统集成

・ 单元测试

・ 系统测试

・ 校正性的维护

・ 功能强化

如果你以前从事过一些不太正规的研制工作,你可能以为列出的这个表有些太详细了。而 如果你从事过一些正式的项目,你就会认为这个表非常准确。在正规性与随意性之间达到平衡 是非常困难的.这会在以后章节中讨论。

如果你是自学编程员或是主要从事非正规研制工作,你很可能还没有意识到这些在生产软 件中所需要的工作步骤。在潜意识中,你把这些工作统统称为编程。在非正式项目中,当你在 考虑设计软件时,你所想到的主要活动可能就是研究者们所指的“创建”工作。

关于“创建”的直觉概念是非常准确的,但它往往缺乏正确观点。把创建活动放到与其相

(14)

关活动的背景中,有助于我们在适当重视其它非创建工作的同时,把主要精力放在正确的任务 上。图 1-1 中给出了创建活动在典型软件生存周期循环中的地位和包括的范围。

正如图中所指出的,创建活动主要指编码和调试过程,但也包括详细设计和测试中的某些 工作。假如这是本关于软件开发所有方面的书,它应该涉及到开发过程所有方面并给予同等重 视。但因为这是一本关于创建技术的手册,所以我们只重点论述创建活动及相关主题。如果把 这本书比喻成一只狗,那么它将用鼻子轻擦创建活动,尾巴扫过设计与测试,而同时向其它开 发活动汪汪叫。

创建活动有时被称作“实现”,它有时被叫作“编码和调试”,有时也称之为“编程”。“编 码”实在不是一个很好的叫法,因为它隐含着把已经设计好的程序机械地翻译成机器语言的过 程;创建则无此含义,它指的是在上述过程中的创造性和决策性活动,在本书中,将交替使用

“实现”、“编程”和“创建”。

图 l-l 软件生存周期中软件开发过程的平面图

在图 1-1 中,给出了软件开发过程的平面图示,而在图 1-2 中,则给了它的立体图示。

图 1-1 和图 l-2 是创建活动的总体图示,但是,什么是它的细节呢?下面是创建活动 中所包含的一些特定任务。

・ 验证基础工作已经完成,可以进行创建工作

(15)

・ 设计和编写子程序与模块

・ 创立数据类型并命名变量

・ 选择控制结构并组织语句块

・ 找出并修正错误

・ 评审其它小组的细节设计和代码,同时接受其它小组评审

・ 通过仔细地格式化和征集意见改进编码

・ 对分别完成的软件单元进行综合

・ 调整编码使其更小、更快

图 1-2 本书主要详细论述详细设计、编码、调试和单元测试(所占比例如图示)

要想更

些是创建活动呢?”。一般认 为,

1.2 软件创建的重要性

正如我们所知,改进软件质量、提高软件生产率是非常重要的。当今世界许多激动人心的 工程

同,创建活动在整个开发活动中所占 时间

件开发中处于枢纽地位。分析和设计是创建活动的基础工作,对系统进行测 试以

ikson 和 详尽地了解创建活动,请参阅目录中每一章的标题。

创建活动包括如此众多的工作,人们可能会禁不住要问:“哪

非创建活动包括:管理活动、需求分析、软件总体设计、用户交互界面设计、系统测试、

维护工作等。这其中每项工作都与创建工作一样,会直接影响到项目的最终成败(那些需要两 个人以上合作至少一星期项目的成败)。关于这其中每一项活动都有很不错的论著,在本书每一 章后都列出这些书的书名。

计划中,软件都被广泛地应用:太空飞行、航空、医学与生命保障科学、电影特技、金融 信息的快速处理、科学研究等,这仅是其中的几个例子。如果读者您也认为软件开发是重要的,

那么您就会问,为什么创建活动是重要的?原因如下:

创建活动是开发软件的重要组成部分。随项目规模不

为 30%~80%之间,在任何计划中占有如此大时间比例的活动必然会影响计划的成败,这 是不言而喻的。

创建活动在软

证实创建活动是正确的则是其后续工作,因而创建活动是软件开发的核心工作。

把主要精力集中于创建活动,可以极大地提高程序员的生产效率。由 Sackman、Er

(16)

Grant 在 1968 年进行的实验表明,每个程序员的效率系数的变化范围为 10~20,这一结果随后 又被其它几个实验所证实。最优秀程序员与普通程序员的巨大差异表明,普通程序员提高效率 的潜力是非常大的。

创建活动的产品,源代码,往往是软件的唯一精确描述。在许多项目中,程序员可得到的 唯一文件便是代码本身。需求说明和设计文档可能会过时,但源代码却总是最新的。因此,源 代码必须具有最好的质量。一个软件成功与否的关键,就在于是否不断运用技术来改进源代码。

而这些技术恰恰是在创建阶段,才能得以最有效的应用。

创建活动是唯一一项必不可少的工作。理论上一个软件项目要经过精心的需求分析和总体 设计,然后再进行创建,接着对其进行彻底的、严格的系统测试。然而,实际工作中的软件项 目,往往越过前两个阶段而直接进行创建活动,最后,由于有太多的错误要修改,系统测试又 被弃之路旁。但是,不管一个项目的计划多么疏漏而又如何匆忙,创建活动都是必不可少的。

无论怎样精简,改进创建活动都是改进软件开发工作的方法。

l.3 小 结

・ 创建活动是总体设计和系统测试之间承上启下的工作。

・ 创建活动主要包括:详细设计、编码、调试和单元测试。

・ 关于创建活动的其它称谓有:实现、编程等。

・ 创建活动质量对软件质量有潜在影响。

・ 在最后的分析中,对创建活动理解的好坏,决定了一个程序员素质的高低,这将在 本书其余部分论述。

(17)

第二章 利用隐喻对编程进行更深刻的理解

目录

2.1 隐喻的重要性

2.2 如何使用软件隐喻(Software MetaPhors)

2.3 通常的软件隐喻 2.4 小结

相关章节

设计中的启发:“设计是一个启发过程”见 7.5 节

计算机科学的语言可能是所有科学领域中最丰富的。想象一下。你走进一间干净整洁、温 度严格控制在 68℉的房间,在这里,你将会找到病毒、蠕虫、臭虫、炸弹、崩溃、火焰、扭曲 的变形者、特洛伊木马和致命错误,在其它领域中,你会遇到这种情况吗?

这些形象的隐喻描述了特定的软件现象。同样形象的隐喻描述了更为广泛的现象,你可以 利用它们来加深你对软件开发的理解。

本书其余部分与本章关于隐喻的论述无关,如果你想了解实质问题可以跳过这一章。但你 要想对软件开发有更清楚的理解,请阅读这一章。

2.1 隐喻的重要性

重大发现往往是从类比中产生的。通过把一个你所陌生的事物与你所熟知的事物比较,你 会对它有进一步的认识,从而形成你对它的独到的深刻理解,这种隐喻方法被称之为“模型化”。

在科学发展史上,充满了利用类比而产生的发现。化学家 Kekle 梦见一条蛇咬住了自己的尾巴,

醒来后,他由此联想到苯的结构,提出了苯是环形分子的假说,这一假说在 1966 年被 Barbour 用实验所证实。

分子运动论是在“保龄球”模型上建立起来的。在这里,分子被假想为具有质量并且与保 龄球一样相互之间进行完全弹性碰撞的小球,并且在此基础上,又产生了许多有用的模型。

光的波动理论是在与声音类比的基础上产生的。光与声都具有振幅(亮度与音量),频率(颜 色与音调)和其它类似性质。这种类比是如此有效,以致于科学家们花费了大量时间来寻找像 空气传播声音一样传播光的物质——“以太”,但他们从来也没能找到。有时如此有效的类比这 次却导出了错误结果。

通常,模型的力量在于它能提供生动形象的概念而易被人整个接受。并提供特性、联系和 附加的疑问,有时模型会提出令人困惑的问题,这时往往是由于模型被误解了,那些建筑“以 太”的科学家们,就是因为误解了模型。

正如你所预料的,有些模型比其它的要好。好的模型要简单、与其它模型关联密切、能解释

(18)

大部分实验事实和观测现象。

比如一个悬在铁链上来回晃动的大石头。在 Galileo 之前,Aristotelian 看到它时想到的是重 物必然要从高处落下来停在低处,他认为石头是在克服阻力下落,而当 Galileo 看到同一现象时,

他认为自己看到了一个单摆,他认为石头是在不断地重复同一运动。

这两个模型所提供的信息是截然不同的。Aristotelian 认为石头是在下落,因而他关心的是 石头的重量、升起的高度及停下所需的时间。而 Galileo 从单摆模型出发,他关心的是石头的重 量、铁链的半径、石头的角位移及石头每摆一次所需要的时间。Galileo 之所以能发现单摆定律,

就是因为他的模型与 Aristotelian 不同,从而导致他们提出了不同的问题。

隐喻对加深软件理解所做出的贡献,与它对其它领域所做出的贡献一样大。1973 年,在图 灵奖颁奖演说中,Charles Bachman 叙述了从地心说向日心说转移的过程。Ptolemy 的地心说统 治了近 1400 年。直到 1543 年,Copernicus 提出了日心说,这一思想模型的转变导致了一系列 新星的发现,把月亮定义为卫星而不是行星,也改变了人类对自身在宇宙中地位的理解。

Bachman 把天文学中从地心说向日心说的转变,与 70 年代前期在计算机编程中的变化作了 个对比。在当时,数据处理正从以计算机为中心向以数据库为中心进行转变。Bachman 指出,

在旧的处理模式中,数据被当成是一个连续流过计算机的卡片流(以计算机为中心);而在新的 模式中,数据好比是一个水池,而计算机则偶尔涉足其中(以数据库为中心)。

今天,很难想象谁会认为太阳绕着地球转;也同样难以想象谁会把数据当成流过计算机的 卡片流。在这两个例子中,旧的理论一旦被抛弃,很难想象有谁会再把它捡起来。具有讽刺意 味的是,旧理论的相信者认为新理论荒唐可笑,就像我们今天看旧理论一样。

当日心说出现之后,地心说便成了那些相信它的天文学家的阻碍。同样,计算机中心模式 也已经成了那些相信它的计算机科学家的阻碍,因为我们现在已经有了数据库中心模式。

如果一旦看了新的模型,我们便说:“哦,当然正确的模型更有用,其余的都是错误的”,

那只会降低模型的作用。因为这太偏激了。科学史并不是由一系列从“错误”模型到“正确”

模型开关组成的,而是逐渐由“坏的”模型变为“较好”的模型,从包含面较窄到包含面较宽,

从覆盖领域较少到覆盖领域较多。

事实上,很多被较好模型替代的旧模型仍然在发挥作用。例如,工程师们仍然在用牛顿力 学进行工程计算,虽然它已经被相对论力学所取代。

软件科学是一门比其它学科年轻得多的学科,还很不成熟,远未形成一套标准的模型。所 以,现在拥有的是大量相互矛盾的模型。这其中有些很好,有些则很差。因此,对这些模型理 解得好坏,便决定了你对软件开发理解的好坏。

2.2 如何使用软件隐喻

软件隐喻更像是一束搜索灯光,而不是一张地图,它并不会告诉你到哪里去寻找答案;它 只给你以启发,教你如何寻找答案,而不是像数学算法一样硬性规定出到哪里找出答案。

一个公式是一套完整建立的、进行某一些任务的规则。它的结果是可以预测的、确定的,

并不取决于运气。公式会告诉你直接从 A 点走到 B 点,中间不准绕路,不准随意顺便访问 C、

D、E 或 F 点,也不准停下来闻一下玫瑰花香或者喝杯咖啡什么的,一切必须按规定来。

启发是一种帮助你寻求答案的技术。它的结果往往和运气有关,因为它只告诉你如何去

(19)

找,而并未告诉你应该找到些什么。它不会告诉你怎样直接从点 A 到点 B.甚至很可能它根本 就不知道点 A 和点 B 在哪里。事实上,可以认为启发是一个穿着小丑儿外套的公式。它往往不 可预测,更富有趣味,不会保证一定会发生或不会发生什么。

比如,开车去某人家的公式是这样的:沿 167 号公路向南到 Sumner,从 Bonney 湖出口向 山上开 2.4 英里,借助加油站的灯光向左拐,在第一个右转弯处向右转,再拐入通向褐色房子 的公路,寻找的门牌号是北大街 714 号。

以下则是一个如何找到我们房屋的启发:找到我们寄给你的最后一封信,开车到回信地址 所说的小镇,到了镇上后随便问哪个人我们住哪儿,别担心,镇上的人都认识我们。如果你谁 也遇不到的话,就打电话找我们。

公式和启发之间的区别是微妙的,这两个例子或许会说明一些问题。从本书的角度来看,

它们之间的主要区别是:它们与答案之间的直接程度。公式给予直接指令;而启发则告诉你该 怎样找到这些指令,或者至少告诉你到哪里寻找它们。

如果有一套指令告诉你该如何解决程序中的问题,这当然会使编程变得很容易,而且结果 也可以预测了。但是编程科学目前还没有那样发达,也许永远也不会。编程中最富于挑战性的 问题便是将问题概念化,编程中许多错误往往都是概念性错误,因为每个程序在概念上都是独 特的,所以创立一套可以指导每一个问题的规则是非常困难,甚至是不可能的。这样,从总体 上知道该如何解决问题,便几乎和知道某一特定问题的答案一样重要了。

你是怎样使用软件隐喻的呢?应该用它来帮助你获得关于编程过程的内在理解,利用它们 来帮助你考虑编程活动,想象解决问题的更好办法。你不要一看到某一行代码就说这与这一章 所使用的某个隐喻相矛盾。随着时间推移,在编程过程当中使用隐喻的程序员肯定比不使用这 一方法的人编写代码更快更好。

2.3 通常的软件隐喻

随着软件的发展,隐喻越来越多,已经到了使人迷惑的地步,Fred Brooks 说写软件就像耕 种、猎狼或者在一个沥青矿坑中淹死一只恐龙。Paul Heekel 说这就像电影《白雪公主与七个小 矮人》。David Gries 说这是科学,Donald Knuth 则说这是门艺术,Watts Hamphrey 则说这是一个 过程,Peter Freeman 说这是个系统,Harlan Mills 认为这就像解数学题、做外科手术、或者是宰 一条狗,Mark Spinrad 和 Curt Abraham 说这更像是开发西部、在冰水中洗澡或者围着营火吃豆 子。

2.3.l 软件书写:写代码(Writing Code)

开发软件最原始的隐喻出自“写代码”一词。这个写的隐喻说明开发一个程序就像随便写 封信,你准备好纸、笔和墨水,坐下从头写到尾就算完成了。这不需要任何正式计划,你只是 把你要说的都写出来。

许多想法都源于写隐喻。Jon Beitle 说,你应该准备好一杯白兰地,一支上等雪茄,与你喜 欢的猎狗一同坐在火边,像一个优秀小说家一样享受一次“自由编程”。Brian 和 Kernighan 把 写隐喻风格的书称为《风格要素》(《The Elements of Style》)之后,把他们编程风格的书称作《编 程风格要素》(《The Elements of Programming Style》),程序员们则经常谈论程序的“可读性”。

(20)

在一些小问题中,写代码隐喻可以充分描述它们。但是对于其余的问题,它就力不从心了,

它不可能全面彻底地描述软件开发过程。写往往是一种个人活动,而软件开发往往需要许多人 分担各种不同的责任。当你写完一封信时,你把它装进信封并把它寄出去后,你就再也不能改 变它的内容了,无论从哪个角度说,这项工作都已经完成了。软件的内容是很容易改变的却很 难彻底完成。几乎有 50%的软件开发工作量是在软件最初发行之后才进行的(Lientz 和 Swanson,

1980)。编写软件,主要工作量集中在初始阶段。在软件创建中,把精力集中于初始阶段往往不 如在初始工作完成后,再集中精力进行代码的重新调整工作。简而言之,写隐喻往往把软件工 作表示成是一项过于简单而刻板的工作。

不幸的是,写隐喻已经通过我们这个星球上最流行的软件书——Fred Brooks 的《The Mythical Man Month》而变得永存了。Brooks 说,“扔掉一个计划,又有什么呢?”这使得我们 联想到一大堆被扔进废纸篓的手稿。当你写封家常信问候你叔叔时,准备扔掉一封信是可能的,

这也可能是 Brooks1975 年写那本书时,当时软件工程的水平。

但是,到了九十年代,再把写隐喻解释为准备扔掉一封信时,恐怕是不合时宜的。现在,

开发一个主要系统的投资已经相当于建一幢十层办公楼或造一艘远洋客轮的费用了。我们应该 在第一次调试时就完成它,或者在它们成本最低时试几次运气,其它几个隐喻较好地解决了说 明达到这一目的的方法问题。

2.3.2 软件播种:生成系统(Growing a System)

与刻板的写隐喻相反,一些软件开发者认为你应该把创建软件当作播种或培植庄稼。你设 计一小部分,编码一小部分,测试一小部分,然后在某个时候把它加到系统上,通过小步走,

你减小了每次可能遇到的错误。

有时,一项先进的技术可能是通过拙劣的隐喻来表达的。在这种情况下,应努力保留这项 技术并换一个隐喻来表达它。在这里增量技术是先进的,但是种庄稼的比喻则是十分拙劣的。

一次干一点儿的想法可能和植物生长有某种类似之处,但是耕种类比实在太牵强,而且也 令人感到陌生,因而也就很快被后面的隐喻所取代了。很难把耕种隐喻推广到每次做一点儿这 一简单想法之外。如果你来用耕种隐喻,你就会发现自己在谈论给系统计划施肥,减少详细设 计,通过有效地田间管理提高编码产量,最后收获编码。你也会谈论进行轮作,用种小麦代替 大麦,让土地休息一年以提高土壤中的养分。

软件种植隐喻的弱点是你对于软件开发失去了直接控制。你在春天播种代码,最后在秋天 收获一大堆代码。

2.3.3 软件珍珠培植法:系统积累(System Accretion)

有时候,人们在谈论种植软件而事实上他们指的是软件积累。这两个隐喻是密切联系的,

但是软件积累更深刻一些。“积累”这个词,含有通过外加或吸收,缓慢生长的意思,就像河蚌 逐渐分泌酸钙形成珍珠一样。在地质学上,水中悬浮物逐渐沉积形成陆地的过程也与此相似。

这并不是说你要从水中悬浮物里沉积出代码来;这只意味着你应该学会每次向你的系统中 加一点儿东西。另外一个与积累密切相联的词是增量。增量设计、构造、测试是软件开发的最 强有力工具之一。“增量”一词在设计者心目中还远未达到“结构化”或“面向对象设计”等的 地位,所以迄今为止也没有一本关于这方面的论述,这实在是令人遗憾的,因为这种书中所收 集的技术将具有极大的潜力。

(21)

在增量开发中,你首先设计系统可以运行的最简单版本。它甚至可以不接受实际数据输入,

或者对数据进行处理。它也可以不产生输出,只需要成为一个坚实的骨架结构,以便能承受将 要在它之上发展的真实系统。它可以调用任何一个实现预定功能而设立的伪子程序。就像河蚌 刚开始产生珍珠的核——一粒沙子。

当你搭好骨架后,逐渐地往上添加肌肉和皮肤。你把每一个伪子程序变成真正的子程序。

此时你不必再假设产生结果了,你可以随意访问一个代码来产生结果。也不必使其假设接收输 入,你可以用同样的方法让它接收输入。你每次加入一点儿代码直到你最终完成它。

这种方法的发展是令人印象非常深刻的。Fred Brooks,在 1975 年时还认为:“应做好建造 一个扔掉一个的准备”,在 1987 年时,却说在过去的岁月里,还没有一样东西像增量概念这样 如此深刻地改变了他自己的实践或效率。

增量隐喻的力量在于:作为一个隐喻,它并没有过分作出许诺,它不像耕种隐喻那样容易 被错误延伸。河蚌育珍珠的联想对理解增量发展法或积累法有很大帮助。

2.3.4 软件创建:建造软件(building software)

“建造”一词的想象比“写”或者“种植’软件的想象更为贴切,它与“增量”软件的想 法是基本一致的。建造隐喻暗示了许多诸如计划、准备、执行等工作阶段。如果你仔细研究这 个隐喻,你还会发现它还暗示着其它许多东西。

建造一个四英尺高的塔需要一双稳健的手、一个平台和十个完好的啤酒罐。而建造一个四 百英尺高的塔却决不仅仅是需要一千个啤酒罐就够了,它还需要一种完全不同的计划和创建方 法。

如果你想建一个简单的建筑物,比如说一个狗舍,你买来了木板和钉子,到下午的时候,

你已经给你的爱犬造好了一幢新房子,假设你忘了修一个门,不过这没关系,你可以补救一下 或推倒一节重新开始。你所浪费的不过是一个下午的时间罢了。这与小型软件的发展失败非常 类似。如果你有 25 行代码设计错了。那你重新再来一遍好了,你不会因此浪费许多的。

然而如果你是在造一幢房子,那修建的过程就要复杂些了,而拙劣设计的后果也严重得多。

首先,你必须决定造一幢什么样的房子,这就像软件开发中的问题定义。然后,你与建筑师必 须搞出一个你们都同意的总体方案,这和软件的总体设计是一样的。接着,你又画出细节蓝图 并找来一位承包商,这相当于软件中的详细设计。下面的工作是选好房址、打地基、建造起房 屋的框架、建好墙壁并加上屋顶、用千斤锤检查墙壁是否垂直,这同软件创建基本差不多。当 房屋的绝大部分工作已经完成时,你请来园艺师和装修师,以便使你的房间和空地得到最好的 利用,这可以与软件优化相类似。在整个过程中,会有各种监督人员来检查房址、地基、框架、

供电系统和其它东西,这也可以与软件开发中的评审和鉴定相类似。

较大的规模和复杂性往往意味着可以产生较大的成果。在修房子的时候,材料可能比较贵,

但更大的花费是劳动力。拆掉一面墙并把它移到六英尺之外是很昂贵的,但并不是因为你浪费 了许多钉子,而是因为你需要付出劳动。你应该尽可能精心设计,以避免那些本可避免的错误,

以降低成本。在开发软件过程中,材料更便宜,然而劳动力成本却更高。改变一个报告的格式,

可能与移走一幢房子里的墙壁一样昂贵,因为二者成本的主要部分都是劳动力。

(22)

这两个活动之间还有什么类似之处呢?在建房子中,你不会去建造那些你可以现成买来的 东西,比如洗衣机、烘干机,电冰箱、吸尘器等,除非你是个机械迷。同时,你也会去购买已 经做好的地毯、门、窗和浴室用品,而不是自己动手建。如果你正在建造一个软件,你也会这 样做。你会推广使用高级语言的特点,而不是去编写操作系统一级的代码。你也会利用已经存 在的显示控制和数据库处理系统,利用已经通过的子程序。如果样样都自己动手是很不明智的。

如果你想修建一幢陈设一流的别墅,情况就不同了,你可能定做全套家具,因为希望洗碗 机、冰箱等与你的家具协调一致,同时你还会定做别具风格的门和窗户。这种定制化的方式与 一流软件开发也是非常类似的。为了这一目的,你可能创建精度更高、速度更快的科学公式。

你也会设计自己的显示控制、数据库处理系统和自己的子程序,以使整个软件给人以一气呵成,

天衣无缝的感觉。

当然这两种建造方法也要付出代价,工作的每一步都要依据事先制定好的计划进行。如果 软件开发工作的顺序有误,那么这个软件将是难以编码、难以测试和难以调试的。这可能会使 整个计划延误甚至失败,因为每个人从事的工作都非常复杂,把它们综合到一起后会使人无所 适从。

如果你在盖办公楼时工作做得不好,那么在楼内办公的人便可能面临危险。同样,如果你 在创建医药、航空电子、空中交通管制、加工控制等软件时工作做得不好,后果也可能是灾难 性的。危及别人生命是劣质软件的最可怕后果,但并不是它的唯一危害。如果公司的股东们因 为你编写了错误软件而赔钱,那也是令人遗憾的。无论如何,无辜的人们没有义务为你的工作 失误而付出代价。

对于软件作修改与建造建筑物也有类似之处。如果你要移走的那面墙壁还要支撑其它东西 而不仅仅是隔开两个房间,那么你要付出的成本将会更高。同样,对软件做结构性的修改也将 比增加或减少外设特征付出更高昂的代价。

最后,建筑类比对于超大型软件也是同样适用的。一幢超大型建筑物存在错误的后果将是 灾难性的,整个工程可能不得不返工。建筑师们在制定和审查计划时是非常仔细的,他们往往 留出安全裕度,多用 10%的材料来加强结构总比一幢大楼坍塌要好得多,同时还必须仔细注意 工时计划,在修建帝国大厦时,每辆卡车的每次卸货时间都留出了十五分钟的裕度。因为如果 有一辆卡车不能在指定时间到达指定的位置,整个计划就有可能被延误。

同样,对于超大型软件来说,计划工作需要比一般的大型软件在更高的层次上进行。1977 年,Capers Jones 估计说,对于一个拥有 750,000 行代码的系统来说,可能需要多达 600 页的 功能定义文件。对于一个人来说,不要说理解这种规模全部的设计,就是读完它也是非常困难 的。安全系数对于这种项目是必须的,制定该系统的工时计划尤为重要。当我们在建造与帝国 大厦同等经济规模的软件时,我们也需要同等严密的计划。而我们现在才刚刚开始考虑这种规 模项目的计划技术。

这两者之间的相似还可以推广到其它方面,这就是为什么建筑物创建隐喻是如此强有力的 原因。许多常用的软件词汇来源于建筑学,如:软件体系结构、搭结构架、构造、分割代码、

插入子程序等等。

2.3.5 实用软件技术:智能工具箱(The Intellectual Toolbox)

在过去的十几年中,优秀的软件开发人员们积累了几十条关于开发软件的技术和技巧,有

(23)

些像咒语般灵验,这些技术不是规则,它们是分析工具。一个优秀的工匠知道用什么样的工 具干哪一样工作,而且知道该如何使用它们。程序员也是如此,关于编程你理解得越深入,

你的工具箱里的工具也就越多,何时何地该如何运用它们的知识也就越多。

把方法和技巧当作工具是很有益处的,因为这样可以使我们对其有一个正确的态度。不 要把最新的“面向对象设计技术”当作上帝赐予的法宝,它不过是一件在某些场合下有用,

而在某些场合下又无用的技术。如果你拥有的唯一工具就是一把锤子,那么你就会把整个世 界都当作一个钉子。好在没有人会花 500 美元一天的费用来雇佣一个仅告诉你去买一把可以 解决一切问题的锤子的研究小组,也没有人建议你丢掉你的改锥、手钻和电烙铁。

在软件开发中,常常会有人告诉你用一种方法来代替另外一种方法。这实在不幸,如果 你仅仅采用一种方法,那你就会把整个世界都当成那个工具的作用对象。你会失去用更适合 的方法解决问题的机会。工具箱隐喻有助于我们保留一切方法、技巧、技术等,并在适当的 时候使用它们。

2.3.6 复合隐喻(Combing Metaphors)

因为隐喻更像是一种启发,而不是公式,所以,它们并不是互相排斥的。你可以同时使 用增量隐喻和建筑隐喻。如果你愿意的话,你也可以采用“写”隐喻,或者把写隐喻与耕种 隐喻一起使用。只要能激发你的思想,你尽可以采用一切你认为合适的隐喻。

使用隐喻是一项模糊的事情。你不得不把它们外推到可以从中受到启发的外延中。如果 你把它过分外推或者推广到了错误方向,它很可能使你误入歧途。就像是再好的工具也有可 能被误用一样,你也可能错误使用隐喻。但是,它们的作用将无可置疑地使其成为你的智能 工具箱中的一件有力工具。

2.4 小 结

隐喻仅仅是启发,而不是公式,因此,它们更倾向于比较随便,无拘无束。

・ 隐喻通过把软件开发与你所熟知的事情联系在一起,从而使你对其有更深刻的理解。

・ 一些隐喻要好于其它隐喻。

・ 把软件创建与建造建筑物类比,表明开发软件前要精心准备,并表明了大规模项目与小 规模项目之间的差别。

・ 认为软件开发实践是智能工具箱中的工具进一步表明,每个程序员都有许多自己的工 具,没有任何一种工具是万能的。为每件工作选择合适的工具,是成为一个优秀程序员 的首要素质之一。

(24)

第三章 软件创建的先决条件

目录

3.1 先决条件重要性 3.2 问题定义先决条件 3.3 需求分析先决条件 3.4 结构设计先决条件 3.5 选择编程语言先决条件 3.6 编程约定

3.7 应花在先决条件上的时间 3.8 改变先决条件以适应你的项目

3.9 小结

相关章节

・ 不同规模程序的不同条件:见第 21 章

・ 管理创建:见第 22 章

・ 设计:见第 7 章

在开始修造一幢房屋之前,建筑工人会评审蓝图,确认所有用料已经备齐,并检查房子的 地基。建筑工人为修建摩天大楼和修建狗舍所做的准备工作是截然不同的。但不管是什么样的 项目,准备工作总是和需要相适应的,并且应在工程正式开始前做完。

本章主要论述在软件创建之前所要做的准备工作,对于建筑业来说,项目的成败往往在开 工前就已经决定了。如果基础打得不好,或者项目计划进行得不充分,你所能做的最多也就是 防止计划失败,根本谈不上做好。如果你想做一件精美的首饰,那么就得用钻石作原料。如果 你用的是砖头,那你所能得到的最好结果不过是块漂亮的砖头而已。

虽然本章讲的是软件创建基础工作,但并没有直接论述创建工作。如果你觉得不耐烦,或 是你对软件工程生存期循环已经很熟悉了,那么请跳过本章而直接进入下一章。

3.1 先决条件重要性

优秀程序员的一个突出特点是他们采用高质量的过程来创建软件。这种过程在计划的开始、

中间和末尾都强调高质量。

如果你只在一个计划即将结束时强调质量,那你注重的只是测试。当某些人一谈起软件质 量时,他们首先想到的便是测试。然而,事实上测试只是全部质量控制策略的一部分。而且并 不是最重要的部分。测试既不能消除在正确方向上的错误工作,也不能消除在错误方向上的正 确工作的错误,这种错误必须在测试开始之前就清除掉,甚至在创建工作开始之前就要努力清 除掉它们。

(25)

如果你在一个计划的中间强调质量,那么你强调的是创建活动,这一活动是本书论述的中 心。

如果在一个计划的开始强调质量,这意味着你计划并要求设计一种高质量的产品。假设你 在过程开始时要求设计的是一种菲亚特汽车,你尽可以用你所喜欢的各种手段测试它,但是无 论你怎样测试,它也决不会变成一辆罗尔斯——罗伊斯牌汽车。或许你所得到的是一辆最好的 菲亚特汽车,但如果你想要的是罗尔斯——罗伊斯车,你就不得不从计划开始时就提出要求。

在软件开发中,当你进行诸如问题定义、规定解决办法等等计划工作时,你所进行的就是这样 的工作。

由于创建工作处在一个计划的中间,所以,当你开始创建工作时,早期的工作已经奠定了 项目成败的基础。在创建工作中,至少你应该知道自己的处境如何,当你发现失败的乌云从地 平线上升起时,赶快返回第一阶段。本章其余部分主要讲述准备工作已经作好了。

3.1.l 造成准备不足的原因

你也许会认为所有的职业程序员都懂得准备工作的重要性,并且在开始正式工作之前确认 所有的先决条件都已得到满足。不幸的是,事实并非如此。

一些程序员并不作准备工作,因为他们抵制不了立刻开始进行编码工作的渴望。如果你就 是这种程序员,那我对你有两条忠告。第一,阅读一下下一部分工作的内容提示,或许你会从 中发现一些你没想到的问题。第二,要注意自己的问题。只要创建过几个大的程序,你就会明 白强调准备工作的必要性。不要忘记自己的经验教训。

程序员不重视准备工作的另一个原因是管理人员往往不理解那些在创建先决条件上花费时 间的程序员。 Ed Yourdon 和 Tom DeMarco 等人强调准备工作已经有十五年了。在这期间,他 们不时地敲响警钟,或许有一天,管理人员们最终会明白软件开发不仅仅是编写代码。

八十年代后期,我曾经在一项军用项目的某一部门中工作。当项目进行到需求分析阶段时,

负责这个计划的一位将军前来视察。我们告诉了他目前所处的阶段,并主要谈论了文件编写工 作,而这位将军却坚持要看一下代码,我们告诉他目前还没有代码,而他却走进一间正有一百 多人工作的房间,转了一圈,企图找到谁在编码。由于未能如愿以偿,他变得有些气急败坏,

这位身材高大的将军指着自己身边的工程师喊道:“他在干什么?他一定是在写代码。”事实上,

这位软件工程师正在进行文档格式编排的工作,由于这位将军想得到代码,认为那看起来像代 码并且想让工程师编码,所以我们不得不骗他说这位工程师写的确实是代码。

这可以称为 WISCA 或 WIMP 现象,即:为什么 Sam 没有正在写代码?或 Mary 为什么没 正在编程?

如果你正在从事的项目经理像那个将军一样,命令你立刻开始编码,说声“是,长官”是 很容易的。但这是一个坏的反应,你应该还有几个替代办法。

第一,你应该平静地拒绝按照错误顺序工作。如果你与老板的关系很正常的话,那么这太 好了。

第二,你可以假装正在编码而事实上没有。把一个旧的程序清单放到桌角上,然后埋头从 事你的需求和构想文件编写工作,不管你的老板同不同意。这样你可以把工作做得更快更好。

从你老板的观点来看,这个忽视是一个福音。

第三,你可以用技术项目的开发方式来教育一下老板。这是一个好办法因为这可以增加这

(26)

个世界上开明老板的数量。在下一部分,我们将给出更多在创建活动前做好准备工作的理由。

最后,你可以另找一份工作。优秀的程序员是非常短缺的。可以找到更好的工作,干吗非 要呆在一个很不开明的程序店里徒损生命呢?

3.1.2 在进行创建工作之前必须做准备工作的论据

假设你已经登上了问题定义的山峰,与负责需求分析的人并肩走了一英里,在结构设计之 泉中,洗净了你沾满灰尘的衣服,并且沐浴在已经作好准备的纯洁之水中。那么你就会知道在 实现一个系统之前,你应该清楚需要一个系统干什么和需要怎样去干。

作为一个工程技术人员,教育你周围的人,让他们懂得技术项目的开发过程,也是你工作 的一部分。本书的这一部分可以帮你对付那些还不懂得技术项目开发过程的老板和管理人员。

它是关于进行构造设计和问题定义设计权利的延伸论据。在你进行编码、测试和调试之前,学 会这些论据,并且和你的老板推心置腹地谈谈技术项目的开发过程。

求助于逻辑推理

进行有效程序设计的关键之一就是认识到准备工作是非常重要的。在进行一项大的项目 之前,事先做好计划是明智的。项目越大,需要的计划工作量也越大,从管理人员的角度来看,

计划是指确定一个项目所需要的时间、人力、物力和财力。从技术人员的观点来看,计划是指 弄清楚你想要干什么,以免做出错误的工作而徒耗精力与钱财。有时候你自己并不十分清楚自 己想要的到底是什么?起码刚开始是这样。这时,就会比清楚知道用户需求的人要付出更多努 力,但是,这总比做出一件错误的东西,然后把它扔掉,再从头开始的成本要低得多。

建造一个系统之前,弄清楚怎样开始和如何建造它也是非常重要的,你当然不希望在完全 没有必要的情况下,浪费时间与钱财去钻死胡同而白白增加成本。

求助于类比

创建一个软件系统与其它需要耗费人力与财力的工程是一样的。如果你要造一幢房子,在 开始砌第一块砖之前,你必须事先画好建筑图与蓝图。在你开始浇铸水泥之前,你必须让人评 审你的蓝图并获得通过,在软件开发中事先做计划也与此类似。

在你把圣诞树立起来后,你才会开始装饰它,在没有修好烟囱之前你也不会点燃炉火的,

同样,也没有人会打算在油箱空空的情况下踏上旅程,在软件开发中,你也必须按照正确的顺 序来进行。

程序员处于软件开发食物链的最后一环。结构设计吃掉需求分析;详细设计者以结构设计 者为食,而他自己又成为编码者的食物。

比较软件食物链和真正的食物链,我们会发现如下事实,在一个正常的生态系统中,海鸥 以沙丁鱼为食,沙丁鱼吃鲜鱼,鲜鱼吃水虱,其结果会形成一个正常的食物链。在编程工作中,

如果软件食物链的每一级都可以吃到健康的食物,其结果是由一群快乐的程序员写出的正确代 码。

在一个被污染了的环境中,水虱在受到核沾染的水中游泳,鲫鱼体内积聚了滴滴涕,而沙 丁鱼生活的水域又遭受了石油污染,那么,不幸的海鸥由于处在食物链的最后一环,因此,它 吃的不仅仅是沙丁鱼体内的石油,还有鲜鱼体内的滴滴涕和水虱体内的核废料。在程序设计中,

(27)

如果需求定义遭受了污染,那么这又会影响结构设计,而这将最终影响创建活动。这将导 致程序员们脾气暴躁而营养不良,同时生产出遭受严重污染而充满缺陷的软件。

求助于数据

过去十五年的研究证明,一次完成是最好的选择,不必要的修改是非常昂贵的。

TKW 的数据表明,在项目的初期阶段进行设计更改,比如在需求定义和结构设计阶段进行 更改,与在项目的后期,即创建和维护阶段进行更改相比较,其成本要低 50 到 100 倍(Boehm 和 Pappecio,1988)。

对 IBM 的研究也表明了同样结果。在设计开始阶段,如详细设计、编码或单元测试阶段就 消除错误,其成本要比在后期即系统测试和功能强化阶段低 10 到 100 倍(Fagan,1976)。

通常的准则是,一旦引入错误,就尽早发现和消除它。错误在软件食物链中存留的时间越 长,它的危害也就传播得越远。因为需求分析是我们做的第一项工作,因此这时引入的错误在 系统中存留时间最长,危害最大。在软件开发初期引入的错误往往比后来引入的错误传播的面 更广,这也使得早期错误会极大地提高成本。

由 Robert Dunn 总结的表 3-1,给出了由于错误引入和发现时间不同,而产生修复它们所 要耗费的相对成本差异。

表 3-1 错误引入时间

错误发现时间 需求分析 细节设计 编码 需求分析 1 — — 细节设计 2 1 — 波动测试 5 2 1 结构测试 15 5 2 功能测试 25 10 5

表 3-1 的数据表明,在需求分析阶段引入的错误,如果马上发现并消除所耗费的成本是 1000 美元的话,那么如果到了功能测试阶段才发现和消除,耗费的成本则会高达 25000 美元。这说 明我们应该尽早地发现并消除错误。

如果你的老板不相信这些数据,那你可以告诉他,立刻开始编码的程序员往往要比那些先 作计划、而后才编码的程序员花费更长的时间,由 NASA 计算机科学公司和马里兰大学联合建 立的软件工程实验室的研究表明,过分地使用计算机(进行编辑、编译、链接、测试等)往往 与低生产率紧密相联。而在计算机旁花费较少时间的程序员,往往更快地完成工作。这是由于 频繁使用计算机的程序员在进行编码和测试之前,花在计划和设计上的时间较少。

老板的意愿测试

当你认为老板已经理解了在开始创建工作之前进行准备工作的重要性,那么请进行下面 的测验以证实这一点。

下面这些说法哪些是正确的?

・ 我们最好马上就开始编码因为我们将会有许多测试工作要做。

參考文獻

相關文件

– The The readLine readLine method is the same method used to read method is the same method used to read  from the keyboard, but in this case it would read from a 

• Suppose the input graph contains at least one tour of the cities with a total distance at most B. – Then there is a computation path for

● develop teachers’ ability to identify, select and use appropriate print and non-print texts of a variety of text types and themes to enhance students’ motivation and confidence in

Asymptotic Series and Borel Transforms Revisited Alien Calculus and the Stokes Automorphism Trans–Series and the Bridge Equations Stokes Constants and Asymptotics.. 4 The Airy

another direction of world volume appears and resulting theory becomes (1+5)D Moreover, in this case, we can read the string coupling from the gauge field and this enables us to

• An algorithm for such a problem whose running time is a polynomial of the input length and the value (not length) of the largest integer parameter is a..

• By definition, a pseudo-polynomial-time algorithm becomes polynomial-time if each integer parameter is limited to having a value polynomial in the input length.. • Corollary 42

add ax,[edi] ; add an integer add edi,TYPE intarray ; point to next loop L1 ; repeat until ECX = 0 The following code calculates the sum of an array of 16-bit integers... Copying