0%

代码大全

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

欢迎进入软件创建世界

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

  • 问题定义

  • 需求分析

  • 实现计划

  • 总体设计

  • 详细设计

  • 创建即实现

  • 系统集成

  • 单元测试

  • 系统测试

  • 校正性的维护

  • 功能强化

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

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

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

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

  • 找出并修正错误

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

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

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

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

  • 创建活动是开发软件的重要组成部分。

  • 创建活动在软件开发中处于枢纽地位。

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

  • 创建活动的产品,源代码,往往是软件的唯一精确描述。

  • 创建活动是唯一一项必不可少的工作。

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

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

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

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

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

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

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

如果一旦看了新的模型,我们便说:“哦,当然正确的模型更有用,其余的都是错误的”,那只会降低模型的作用。因为这太偏激了。科学史并不是由一系列从“错误”模型到“正确”模型开关组成的,而是逐渐由“坏的”模型变为“较好”的模型,从包含面较窄到包含面较宽,从覆盖领域较少到覆盖领域较多。

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

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

Paul Heekel 说这就像电影《白雪公主与七个小矮人》。David Gries 说这是科学,Donald Knuth 则说这是门艺术,Watts Hamphrey 则说这是一个过程,Peter Freeman 说这是个系统,Harlan Mills 认为这就像解数学题、做外科手术、或者是宰一条狗,Mark Spinrad 和 Curt Abraham 说这更像是开发西部、在冰水中洗澡或者围着营火吃豆子。

  • 软件书写:写代码(Writing Code)

开发软件最原始的隐喻出自“写代码”一词。这个写的隐喻说明开发一个程序就像随便写封信,你准备好纸、笔和墨水,坐下从头写到尾就算完成了。这不需要任何正式计划,你只是把你要说的都写出来。许多想法都源于写隐喻。Jon Beitle 说,你应该准备好一杯白兰地,一支上等雪茄,与你喜欢的猎狗一同坐在火边,像一个优秀小说家一样享受一次“自由编程”。Brian 和 Kernighan 把写隐喻风格的书称为《风格要素》(《The Elements of Style》)之后,把他们编程风格的书称作《编程风格要素》(《The Elements of Programming Style》),程序员们则经常谈论程序的“可读性”。

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

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

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

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

  • 复合隐喻(Combing Metaphors )

1
2
3
4
5
6
7
8
隐喻仅仅是启发,而不是公式,因此,它们更倾向于比较随便,无拘无束。
· 隐喻通过把软件开发与你所熟知的事情联系在一起,从而使你对其有更深刻的理解。
· 一些隐喻要好于其它隐喻。
· 把软件创建与建造建筑物类比,表明开发软件前要精心准备,并表明了大规模项目与小
规模项目之间的差别。
· 认为软件开发实践是智能工具箱中的工具进一步表明,每个程序员都有许多自己的工
具,没有任何一种工具是万能的。为每件工作选择合适的工具,是成为一个优秀程序员
的首要素质之一。

软件创建的先决条件

或许有一天,管理人员们最终会明白软件开发不仅仅是编写代码。

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

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

在软件开发初期引入的错误往往比后来引入的错误传播的面更广,这也使得早期错误会极大地提高成本。

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

正如随着工作的进行,你对其理解越来越深刻一样,用户对自己想要的东西,也是随着项目的进行而越来越清楚的,这也是需求变动的主要原因。-个从不变更需求的计划,事实上是一个对用户的需求不予理睬的计划。

Barry Boehm 在 1984 年指出:从长远观点来看,重新使用旧软件是提高生产率的首要因素。购买代码可以降低计划、详细设计、测试和调试的工作量。 Caper Jones 在 1986 年报告如果购买的代码从 0 上升到 50%,那么生产率可以提高一倍。

结构设计应该给出使用的主要文件、表和数据结构。同时,还应给出考虑的替代方案并评
审作出的选择。在《Software Maintenance Guidebook》一书中,Glass 和 Noiseux 认为数据结构对系统维护有举足轻重的影响,因而,它应该在经过全盘考虑之后,才能选定(1981 年)。如果某一应用需要维护一个用户识别表,而结构设计又选中了顺序存取表来实现,那它就该解释为什么顺序存取表要好于随机存取表、堆栈和哈希表。在创建阶段,这些信息可以使你对结构设计有一个比较深刻的理解。在维护阶段,这些信息也是非常宝贵的。如果没有它们,你就会有一种看一部不带字幕的外国电影的感觉。

最后,应该遵循数据守恒定律:每一个进入的数据都应该出去,或者与其它数据一道出去,如果它不出去,那它就没有必要进来。

一个好的结构设计应该阐明所有问题。这个表并不是用于指导结构设计的,而只是想提供一种方法,通过它,你可以估计处于软件食物链顶层的程序员可以从食物中获得多少营养。它可以作为建立自己的检查表的起点。同需求定义检查表的使用一样,如果你正在从事一个非正式的项目,那么其中有些条款是不必考虑的。但如果你正在开发一个较大的系统,那绝大部分内容都是非常有用的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
· 软件的总体组织形式是否清晰明了?包括对于结构设计的总体评论与描述。
· 模块定义是否清楚?包括它们的功能及其与其它模块的接口。
· 需求定义中所提出的所有功能,是否有恰当数量的模块覆盖?
· 结构设计是否考虑了可能的更改?
· 是否包括了必要的购买?
· 是否阐明了如何改进重新启用的代码来满足现在的结构设计需求?
· 是否描述并验证了所有主要的数据结构?
· 主要数据结构是否隐含在存取子程序中?
· 规定数据库组织形式和其它内容了吗?
· 是否说明并验证所有关键算法?
· 是否说明验证所有主要目标?
· 说明处理用户输入的策略了吗?
· 说明并验证处理输入/输出的策略了吗?
· 是否定义了用户界面的关键方面?
· 用户界面是否进行了模块化,以使对它所作的改动不会影响程序其它部分?
· 是否描述并验证了内存使用估算和内存管理?
· 是否对每一模块给出了存储空间和速度限制?
· 是否说明了字符串处理策略?是否提供了对字符串占用空间的估计?
· 所提供的错误处理策略是不是一致的?
· 是否对错误信息进行了成套化管理以提供一个整洁的用户界面?
· 是否指定了坚固性级别?
· 有没有哪一部分结构设计被过分定义或缺少定义了?它是否明确说明了?
· 是否明确提出了系统目标?
· 整个结构在概念上是否是一致的?
· 机器和使用实现的语言是否顶层设计依赖?
· 给出做出每个重要决定的动机了吗?
· 你作为系统实现者的程序员,对结构设计满意吗?

在一种语言的表达能力和其所能思考的问题之间存在着联系,你思考某一问题的能力取决于你所懂得的关于这一问题的词汇。如果你不懂那些词汇,那你也就不能表达那些思想,你甚至根本无法形成那些思想。

程序员也可能同样受到他所懂得的程序语言限制。在某种程序语言方面你所懂得的词汇,当然会决定你如何表达你的编程想法,还很可能决定你将表达什么样的思想。

程序语言影响程序员的思想方法。一个典型的故事是这样说的:“我们正用 Pascal 语言开发一个新的系统,而我们的程序员们却并不熟悉 Pascal 语言,他们都是搞Fortran 语言出身的。结果他们写出的是用 Pascal 编译的代码,但是他们真正使用的却是变形的 Fotran 语言。他们用Fortran 的不好的特性(goto 语句和全局数据)歪曲了 Pascal 语言,而同时又把 Pascal 丰富的控制和数据结构弃之不用”。这种现象在整个软件业都有报道(Hanson 1984,Yourdon 1986)。

在创建工作开始之前,一定要写明你将要采用的编程约定、约定说明一定要写得非常详尽,使得在编程过程中无法对其进行改动。本书提供了许多非常详细的约定。

用于问题定义、需求分析和软件结构设计的时间,随项目需要的不同而不同。一般来说,一个运行良好的项目通常把 20~30%的时间用于先决条件。这 20~30%的时间中不包括进行详细设计的时间,因为它是创建活动的一部分。

· 如果想开发一个高质量的软件,必须自始至终重视质量问题。在开始阶段强调质量往往比在最后强调质量更为有效。

· 程序员的份内工作之一便是向老板和同事宣传软件的开发过程,包括在编程开始前从事先决条件准备工作的重要性。

· 如果问题定义工作做得不好,那么在创建阶段,所解决的问题可能并不是用户真正要解决的问题。

· 如果需求分析工作做得不好,很可能因此而漏掉要解决问题中的重要细节。在创建工作后更改需求,要比在需求分析阶段进行更改的成本高 20 到 100 倍。所以,在开始编程前一定要确认需求定义工作一切正常。

· 在编程前规定好约定,在创建工作结束后再改变代码来满足约定几乎是不可能的。

· 在创建活动开始之前如果无法完成准备工作,可以尝试在不太稳固的基础上进行创建活动。