第四章程序编码 复习要求 1.了解什么是结构化程序设计,以及结构化程序设计的原则 2.了解程序设计风格4个方面的要求。 3.了解提高程序效率的方法。 4.了解程序设计语言的分类和特点。 5.掌握度量程序复杂性的 McCabe方法和 Halstead方法 二、内容提要 1.结构化程序设计 结构化程序设计技术是60年代中期提出来的,它主要包括两个方面: 在编写程序时,强调使用几种基本控制结构,通过组合嵌套,形成程序的控制结构 尽可能避免使用会使程序质量受到影响的GOTO语句 在程序设计过程中,尽量采用自顶向下和逐步细化的原则,由粗到细,一步步展开 (1)结构化程序设计的原则 使用语言中的顺序、选择、重复等有限的基本控制结构表示程序逻辑 选用的控制结构只准许有一个入口和一个出口。 程序语句组成容易识别的块,每块只有一个入口和一个出口 ■复杂结构应该用基本控制结构进行组合嵌套来实现。 语言中没有的控制结构,可用一段等价的程序段模拟,但要求该程序段在整个系统中 应前后一致。 严格控制GOTO语句,仅在用一个非结构化的程序设计语言去实现一个结构化的构 造,或者在某种可以改善而不是损害程序可读性的情况下才可以使用GOTO语句。 大量采用GOIO语句实现控制路径,会使程序路径变得复杂而且混乱,因此要控制 GOIO语句的使用。但有时完全不用GOIO语句进行程序编码,比用GOIO语句编出的程 序可读性差。例如,在査找结束时,文件访问结束时,出现错误情况要从循环中转出时,使 用布尔变量和条件结构来实现就不如用GOIO语句来得简洁易懂 对于常用的高级程序设计语言,一般都具备前述的几种基本控制结构。即使不具备等同 的结构,也可以采用仿真来实现。下面以 FORTRAN77为例进行说明,参看图41 用 FORTRAN77模拟基本控制结构 判断语句 IF(P)THEN if(p)Sl; else $2; ELSE ENDIE 先判断型循环语句
1 第四章 程序编码 一、复习要求 1. 了解什么是结构化程序设计,以及结构化程序设计的原则。 2. 了解程序设计风格 4 个方面的要求。 3. 了解提高程序效率的方法。 4. 了解程序设计语言的分类和特点。 5. 掌握度量程序复杂性的 McCabe 方法和 Halstead 方法。 二、内容提要 1. 结构化程序设计 结构化程序设计技术是 60 年代中期提出来的,它主要包括两个方面: ▪ 在编写程序时,强调使用几种基本控制结构,通过组合嵌套,形成程序的控制结构。 尽可能避免使用会使程序质量受到影响的 GOTO 语句。 ▪ 在程序设计过程中,尽量采用自顶向下和逐步细化的原则,由粗到细,一步步展开。 (1) 结构化程序设计的原则 ▪ 使用语言中的顺序、选择、重复等有限的基本控制结构表示程序逻辑。 ▪ 选用的控制结构只准许有一个入口和一个出口。 ▪ 程序语句组成容易识别的块,每块只有一个入口和一个出口。 ▪ 复杂结构应该用基本控制结构进行组合嵌套来实现。 ▪ 语言中没有的控制结构,可用一段等价的程序段模拟,但要求该程序段在整个系统中 应前后一致。 ▪ 严格控制 GOTO 语句,仅在用一个非结构化的程序设计语言去实现一个结构化的构 造,或者在某种可以改善而不是损害程序可读性的情况下才可以使用 GOTO 语句。 大量采用 GOTO 语句实现控制路径,会使程序路径变得复杂而且混乱,因此要控制 GOTO 语句的使用。但有时完全不用 GOTO 语句进行程序编码,比用 GOTO 语句编出的程 序可读性差。例如,在查找结束时,文件访问结束时,出现错误情况要从循环中转出时,使 用布尔变量和条件结构来实现就不如用 GOTO 语句来得简洁易懂。 对于常用的高级程序设计语言,一般都具备前述的几种基本控制结构。即使不具备等同 的结构,也可以采用仿真来实现。下面以 FORTRAN77 为例进行说明,参看图 4.1。 基本控制结构 用 FORTRAN77 模拟基本控制结构 判断语句 if ( p ) S1; else S2; IF ( p ) THEN S1 ELSE S2 ENDIF 先判断型循环语句 100 CONTINUE
while(p) IF(P)THEN GOTO 100 END 后判断型循环语句 100 CONTINUE do S; while(p); D)GOTO 100 图41用 FORTRAN77语句实现基本控制结构 (2)程序设计自顶向下,逐步求精 在详细设计和编码阶段,应当采取自顶向下,逐步求精的方法,把一个模块的功能逐步 分解,细化为一系列具体的步骤,进而翻译成一系列用某种程序设计语言写成的程序。 例如,要求用筛选法求100以内的素数。所谓筛选法,就是从2到100中去掉2,3,…,9,10 的倍数,剩下的就是100以内的素数。为了解决这个问题,可先按程序功能写出一个框架 ImaIl (){ 建立2到100的数组A[],其中A[=i 建立2到10的素数表B],其中存放2到10以内的素数 若A=i是B]中任一数的倍数,则剔除A[ 输出A[]中所有没有被剔除的数 上述框架中每一个加工语句都可进一步细化成一个循环语句 main(t 建立2到100的数组A[],其中A[=* A[= /建立2到10的素数表B],其中存放2到10以内的素数喇 B[]=2:B12]=3:B[3]=5:B[4]=7 /若A[=i是BL]中任一数的倍数,则剔除A[ 检查A[j所有的数能否被B整除并将能被整除的数从A中剔除: -3.1 输出A[]中所有没有被剔除的数中- for(i=2:i<=100:i++) 若A[没有被剔除,则输出之 继续对3.1和4.1细化下去,直到最后每一个语句都能直接用程序设计语言来表示为止。 for(i=2;i<=100:计++)A=i B[]=2:B[2]=3:B[3]=5:B[4]=7 /若A[=i是B中任一数的倍数,则剔除A* *检查A[]所有的数能否被B整除并将能被整除的数从A[]中剔除* if (A[/BUBU==AO)A[=0 输出A中所有没有被剔除的数 for(i=2:i<=100:i++) 若A没有被剔除,则输出之
2 while ( p ) S; IF ( p ) THEN S GOTO 100 ENDIF 后判断型循环语句 do S; while ( p ); 100 CONTINUE S IF ( p ) GOTO 100 图 4.1 用 FORTRAN77 语句实现基本控制结构 (2) 程序设计自顶向下,逐步求精 在详细设计和编码阶段,应当采取自顶向下,逐步求精的方法,把一个模块的功能逐步 分解,细化为一系列具体的步骤,进而翻译成一系列用某种程序设计语言写成的程序。 例如,要求用筛选法求 100 以内的素数。所谓筛选法,就是从 2 到 100 中去掉 2,3,…,9,10 的倍数,剩下的就是 100 以内的素数。为了解决这个问题,可先按程序功能写出一个框架。 main ( ) { 建立 2 到 100 的数组 A[ ],其中 A[i]=i; - - - - - - - - - - - - -- - - - - - - - - - - - 1 建立 2 到 10 的素数表 B[ ],其中存放 2 到 10 以内的素数;- - - - - - - - - - - - 2 若 A[i]=i 是 B[ ]中任一数的倍数,则剔除 A[i];- - - - - - - - - - - - - - - - - - - - 3 输出 A[ ]中所有没有被剔除的数;- - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - 4 } 上述框架中每一个加工语句都可进一步细化成一个循环语句。 main ( ) { /*建立 2 到 100 的数组 A[ ],其中 A[i]=i*/ - - - - - - - - - - - - - - - - - - - - - - - - 1 for (i = 2;i <= 100;i++)A[i] = i; /* 建立 2 到 10 的素数表 B[ ],其中存放 2 到 10 以内的素数*/ - - - - - - - - - - - 2 B[1] =2; B[2] = 3; B[3] = 5; B[4] = 7; /*若 A[i]=i 是 B[ ]中任一数的倍数,则剔除 A[i]*/ - - - - - - - - - - - - - - - - - - - - 3 for (j = 1; j <= 4; j++) 检查 A[ ]所有的数能否被 B[j]整除并将能被整除的数从 A[]中剔除;- - - - - - - - 3.1 /*输出 A[ ]中所有没有被剔除的数*/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -4 for (i = 2; i <= 100; i++) 若 A[i]没有被剔除,则输出之;- - - - - - - - -- - - - - - - - - - - - - - - - - - - - - - - - - -4.1 } 继续对 3.1 和 4.1 细化下去,直到最后每一个语句都能直接用程序设计语言来表示为止。 main ( ) { for (i = 2;i <= 100;i++)A[i] = i; B[1] =2; B[2] = 3; B[3] = 5; B[4] = 7; /*若 A[i]=i 是 B[ ]中任一数的倍数,则剔除 A[i]*/ for (j = 1; j <= 4; j++) /*检查 A[ ]所有的数能否被 B[j]整除并将能被整除的数从 A[ ]中剔除*/ for (i = 2; i <= 100; i++) if (A[i]/B[j]*B[j] == A[i])A[i] = 0; /*输出 A[]中所有没有被剔除的数*/ for (i = 2; i <= 100; i++) /*若 A[i]没有被剔除,则输出之*/
f(A]=0) printf(“A[%d=%dn”,i,A[) 自顶向下,逐步求精方法的优点: ①自顶向下,逐步求精方法符合人们解决复杂问题的普遍规律。可提高软件开发的成 功率和生产率 ②用先全局后局部,先整体后细节,先抽象后具体的逐步求精的过程开发出来的程序 具有清晰的层次结构,因此程序容易阅读和理解; ③程序自顶向下,逐步细化,分解成一个树形结构(如图42所示)。在同一层的结点 上做的细化工作相互独立。在任何一步发生错 误,一般只影响它下层的结点,同一层其它结 点不受影响。在以后的测试中,也可以先独立 地一个结点一个结点地做,最后再集成。 ④程序清晰和模块化,使得在修改和重° 逐步细化 新设计一个软件时,可复用的代码量最大 ⑤每一步工作仅在上层结点的基础上做 不多的设计扩展,便于检查 ⑥有利于设计的分工和组织工作 图42程序的树形结构 (3)数据结构的合理化 HMl指出,结构化程序设计主要是想从程序的控制结构入手,消除不适应的、容易引 起混乱的GOTO语句。这只是问题的一个方面,而问题的另一方面,过去没有注意到的是数 据结构的合理化问题,即数据结构访问的规范化,标准化问题。 假如数据结构中常使用数组、指针等数据类型,则对它们必须采取随机访问,这样势必 生访问上的混乱。例如,要访问数组元素A[I,必须先对下标i,j访问,造成访问忽前 忽后,这与GOTO语句造成的混乱类似,同样是有害的。 H Mills指出,解决这一问题的办 法是用栈和队列去代替数组和指针。栈与队列分别是按后进先出(LIFO)和先进先出(FIFO) 的原则进行存取的。在程序中用栈和队列代替数组和指针,用合理的规范的顺序存取代替随 机存取,将克服随机存取带来的麻烦。而且有人做了证明,所有使用数组和指针的程序,都 可以使用栈和队列的程序等价替换 2.程序设计风格 在软件生存期中,人们经常要阅读程序。特别是在软件测试阶段和维护阶段,编写程序 的人与参与测试、维护的人都要阅读程序。因此,阅读程序是软件开发和维护过程中的一个 重要组成部分,而且读程序的时间比写程序的时间还要多。70年代初,有人提出在编写程序 时,应使程序具有良好的风格。 程序设计风格包括4个方面:源程序文档化,数据说明,语句结构和输入/输出方法, 力图从编码原则的角度提高程序的可读性,改善程序质量 (1)源程序文档化 ①符号名的命名 符号名即标识符,包括模块名、变量名、常量名、子程序名、数据区名、缓冲区名等。 这些名字应能反映它所代表的实际东西,应有一定实际意义 名字不是越长越好,过长的名字会使程序的逻辑流程变得模糊,给修改带来困难。所 以应当选择精炼的意义明确的名字,改善对程序功能的理解。 必要时可使用缩写名字,但缩写规则要一致,并且要给每一个名字加注释
3 图 4.2 程序的树形结构 if (A[i] != 0) printf(“A[%d]=%d\n”,i,A[i]); } 自顶向下,逐步求精方法的优点: ① 自顶向下,逐步求精方法符合人们解决复杂问题的普遍规律。可提高软件开发的成 功率和生产率; ② 用先全局后局部,先整体后细节,先抽象后具体的逐步求精的过程开发出来的程序 具有清晰的层次结构,因此程序容易阅读和理解; ③ 程序自顶向下,逐步细化,分解成一个树形结构(如图 4.2 所示)。在同一层的结点 上做的细化工作相互独立。在任何一步发生错 误,一般只影响它下层的结点,同一层其它结 点不受影响。在以后的测试中,也可以先独立 地一个结点一个结点地做,最后再集成。 ④ 程序清晰和模块化,使得在修改和重 新设计一个软件时,可复用的代码量最大; ⑤ 每一步工作仅在上层结点的基础上做 不多的设计扩展,便于检查; ⑥ 有利于设计的分工和组织工作。 (3) 数据结构的合理化 H.Mills 指出,结构化程序设计主要是想从程序的控制结构入手,消除不适应的、容易引 起混乱的 GOTO 语句。这只是问题的一个方面,而问题的另一方面,过去没有注意到的是数 据结构的合理化问题,即数据结构访问的规范化,标准化问题。 假如数据结构中常使用数组、指针等数据类型,则对它们必须采取随机访问,这样势必 产生访问上的混乱。例如,要访问数组元素 A[i][j],必须先对下标 i,j 访问,造成访问忽前 忽后,这与 GOTO 语句造成的混乱类似,同样是有害的。H.Mills 指出,解决这一问题的办 法是用栈和队列去代替数组和指针。栈与队列分别是按后进先出(LIFO)和先进先出(FIFO) 的原则进行存取的。在程序中用栈和队列代替数组和指针,用合理的规范的顺序存取代替随 机存取,将克服随机存取带来的麻烦。而且有人做了证明,所有使用数组和指针的程序,都 可以使用栈和队列的程序等价替换。 2.程序设计风格 在软件生存期中,人们经常要阅读程序。特别是在软件测试阶段和维护阶段,编写程序 的人与参与测试、维护的人都要阅读程序。因此,阅读程序是软件开发和维护过程中的一个 重要组成部分,而且读程序的时间比写程序的时间还要多。70 年代初,有人提出在编写程序 时,应使程序具有良好的风格。 程序设计风格包括 4 个方面:源程序文档化,数据说明,语句结构和输入/输出方法, 力图从编码原则的角度提高程序的可读性,改善程序质量。 (1) 源程序文档化 ① 符号名的命名 符号名即标识符,包括模块名、变量名、常量名、子程序名、数据区名、缓冲区名等。 ▪ 这些名字应能反映它所代表的实际东西,应有一定实际意义。 ▪ 名字不是越长越好,过长的名字会使程序的逻辑流程变得模糊,给修改带来困难。所 以应当选择精炼的意义明确的名字,改善对程序功能的理解。 ▪ 必要时可使用缩写名字,但缩写规则要一致,并且要给每一个名字加注释
在一个程序中,一个变量只应用于一种用途。就是说,在同一个程序中一个变量不能 身兼几种工作 ②程序的注释 夹在程序中的注释是程序员与日后的程序读者之间通信的重要手段。正确的注释能够帮 助读者理解程序,可为后续阶段进行测试和维护,提供明确的指导。因此,注释决不是可有 可无的,大多数程序设计语言允许使用自然语言来写注释,这就给阅读程序带来很大的方便 一些正规的程序文本中,注释行的数量占到整个源程序的1/3到1/2,甚至更多 序言性注释:通常置于每个程序模块的开头部分,它应当给出程序的整体说明,对 于理解程序本身具有引导作用。有些软件开发部门对序言性注释做了明确而严格的规定,要 求程序编制者逐项列出的有关项目包括:程序标题、有关本模块功能和目的的说明、主要算 法、接口说明、有关数据描述、模块位置、开发简历等。 功能性注释:嵌在源程序体中,用以描述其后的语句或程序段是在做什么工作,不 要解释下面怎么做,因为解释怎么做常常是与程序本身重复的,并且对于阅读者理解程序没 有什么帮助 书写功能性注释,要注意:·用于描述一段程序,而不是每一个语句:·用缩进和空行, 使程序与注释容易区别;·注释要正确 ③视觉组织 利用空格、空行和移行,提高程序的可视化程度。 恰当地利用空格,可以突出运算的优先性,避免发生运算的错误。 自然的程序段之间可用空行隔开 对于选择语句和循环语句,把其中的程序段语句向右做阶梯式移行。这样可使程序的 逻辑结构更加清晰,层次更加分明。 (2)数据说明 在编写程序时,需注意数据说明的风格。为了使程序中数据说明更易于理解和维护, 须注意以下几点。 数据说明的次序应当规范化,使数据属性容易查找。 当多个变量名用一个语句说明时,应当对这些变量按字母的顺序排列 如果设计了一个复杂的数据结构,应当使用注释来说明在程序实现时这个数据结构的 固有特点。 (3)语句结构 在设计阶段确定了软件的逻辑流结构,但构造单个语句则是编码阶段的任务。语句构造 力求简单,直接,不能为了片面追求效率而使语句复杂化 在一行内只写一条语句,并且采取适当的移行格式,使程序的逻辑和功能变得更加明 ■程序编写首先应当考虑清晰性,不要刻意追求技巧性,使程序编写得过于紧凑 程序编写得要简单,写清楚,直截了当地说明程序员的用意。 除非对效率有特殊的要求,程序编写要做到清晰第一,效率第二。不要为了追求效率 而丧失了清晰性。事实上,程序效率的提高主要应通过选择高效的算法来实现 ■首先要保证程序正确,然后才要求提高速度。反过来说,在使程序高速运行时,首先 要保证它是正确的 让编译程序做简单的优化 尽可能使用库函数 避免使用临时变量而使可读性下降。 尽量用公共过程或子程序去代替重复的功能代码段
4 ▪ 在一个程序中,一个变量只应用于一种用途。就是说,在同一个程序中一个变量不能 身兼几种工作。 ② 程序的注释 夹在程序中的注释是程序员与日后的程序读者之间通信的重要手段。正确的注释能够帮 助读者理解程序,可为后续阶段进行测试和维护,提供明确的指导。因此,注释决不是可有 可无的,大多数程序设计语言允许使用自然语言来写注释,这就给阅读程序带来很大的方便。 一些正规的程序文本中,注释行的数量占到整个源程序的 1/3 到 1/2,甚至更多。 ▪ 序言性注释 :通常置于每个程序模块的开头部分,它应当给出程序的整体说明,对 于理解程序本身具有引导作用。有些软件开发部门对序言性注释做了明确而严格的规定,要 求程序编制者逐项列出的有关项目包括:程序标题、有关本模块功能和目的的说明、主要算 法、接口说明、有关数据描述、模块位置、开发简历等。 ▪ 功能性注释 :嵌在源程序体中,用以描述其后的语句或程序段是在做什么工作,不 要解释下面怎么做,因为解释怎么做常常是与程序本身重复的,并且对于阅读者理解程序没 有什么帮助。 书写功能性注释,要注意:·用于描述一段程序,而不是每一个语句;·用缩进和空行, 使程序与注释容易区别;·注释要正确。 ③ 视觉组织 利用空格、空行和移行,提高程序的可视化程度。 ▪ 恰当地利用空格,可以突出运算的优先性,避免发生运算的错误。 ▪ 自然的程序段之间可用空行隔开; ▪ 对于选择语句和循环语句,把其中的程序段语句向右做阶梯式移行。这样可使程序的 逻辑结构更加清晰,层次更加分明。 (2) 数据说明 在编写程序时,需注意数据说明的风格。为了使程序中数据说明更易于理解和维护,必 须注意以下几点。 ▪ 数据说明的次序应当规范化,使数据属性容易查找。 ▪ 当多个变量名用一个语句说明时,应当对这些变量按字母的顺序排列。 ▪ 如果设计了一个复杂的数据结构,应当使用注释来说明在程序实现时这个数据结构的 固有特点。 (3) 语句结构 在设计阶段确定了软件的逻辑流结构,但构造单个语句则是编码阶段的任务。语句构造 力求简单,直接,不能为了片面追求效率而使语句复杂化。 ▪ 在一行内只写一条语句,并且采取适当的移行格式,使程序的逻辑和功能变得更加明 确。 ▪ 程序编写首先应当考虑清晰性,不要刻意追求技巧性,使程序编写得过于紧凑。 ▪ 程序编写得要简单,写清楚,直截了当地说明程序员的用意。 ▪ 除非对效率有特殊的要求,程序编写要做到清晰第一,效率第二。不要为了追求效率 而丧失了清晰性。事实上,程序效率的提高主要应通过选择高效的算法来实现。 ▪ 首先要保证程序正确,然后才要求提高速度。反过来说,在使程序高速运行时,首先 要保证它是正确的。 ▪ 让编译程序做简单的优化。 ▪ 尽可能使用库函数。 ▪ 避免使用临时变量而使可读性下降。 ▪ 尽量用公共过程或子程序去代替重复的功能代码段
用调用公共函数去代替重复使用的表达式。 使用括号来清晰地表达算术表达式和逻辑表达式的运算顺序 避免不必要的转移。同时如果能保持程序的可读性,则不必用GOIO语句。 尽量只采用三种基本的控制结构来编写程序。 用逻辑表达式代替分支嵌套 避免使用空的ELSE语句和IF… THEN IE…的语句 避免使用 ELSE GOTO和 ELSE RETURN结构 使与判定相联系的动作尽可能地紧跟着判定 避免采用过于复杂的条件测试 ■尽量减少使用“否定”条件的条件语句。不要让读者绕弯子想。 避免过多的循环嵌套和条件嵌套 不要使GOTO语句相互交叉 避免循环的多个出口。 使用数组,以避免重复的控制序列。 尽可能用通俗易懂的伪码来描述程序的流程,然后再翻译成必须使用的语言 数据结构要有利于程序的简化。 ■要模块化,使模块功能尽可能单一化,模块间的耦合能够清淸晰可见 利用信息隐蔽,确保每一个模块的独立性。 从数据出发去构造程序。 不要修补不好的程序,要重新编写。也不要一味地追求代码的复用,要重新组织 对太大的程序,要分块编写、测试,然后再集成 对递归定义的数据结构尽量使用递归过程 注意计算机浮点数运算的特点,例如,浮点数运算10.0*0.通常不等于1.0。 不要单独进行浮点数的比较。用它们做比较,其结果常常发生异常情况。 避免不恰当地追求程序效率,在改进效率前,要做出有关效率的定量估计。 在程序中应有出错处理功能,一旦出现故障时不要让机器进行干预,导致停工。 此外,对于程序中的变量,标号,注释等,还需要给予一些注意。例如, 变量名中尽量不用数字。 显式说明所有的变量。 确保所有变量在使用前都被初始化。 确保注释与代码完全一致 不仅对代码做注释,而且对每条注释都加以编号。 不注释不好的代码,要重新编写 程序格式的安排应有助于读者理解程序 注释不要过于繁琐。 遵循国家标准 经常反躬自省:“如果我不是编码的人,我能看懂它吗?”考虑它的可理解性达到什 么程度 (4)输入和输出(I/O 输入和输出信息是与用户的使用直接相关的。输入和输出的方式和格式应当尽可能方便 用户的使用。因此,在软件需求分析阶段和设计阶段,就应基本确定输入和输出的风格。系 统能否被用户接受,有时就取决于输入和输出的风格 不论是批处理的输入/输出方式,还是交互式的输入/输出方式,在设计和程序编码时 都应考虑下列原则
5 ▪ 用调用公共函数去代替重复使用的表达式。 ▪ 使用括号来清晰地表达算术表达式和逻辑表达式的运算顺序。 ▪ 避免不必要的转移。同时如果能保持程序的可读性,则不必用 GOTO 语句。 ▪ 尽量只采用三种基本的控制结构来编写程序。 ▪ 用逻辑表达式代替分支嵌套。 ▪ 避免使用空的 ELSE 语句和 IF…THEN IF…的语句。 ▪ 避免使用 ELSE GOTO 和 ELSE RETURN 结构。 ▪ 使与判定相联系的动作尽可能地紧跟着判定。 ▪ 避免采用过于复杂的条件测试。 ▪ 尽量减少使用“否定”条件的条件语句。不要让读者绕弯子想。 ▪ 避免过多的循环嵌套和条件嵌套; ▪ 不要使 GOTO 语句相互交叉。 ▪ 避免循环的多个出口。 ▪ 使用数组,以避免重复的控制序列。 ▪ 尽可能用通俗易懂的伪码来描述程序的流程,然后再翻译成必须使用的语言。 ▪ 数据结构要有利于程序的简化。 ▪ 要模块化,使模块功能尽可能单一化,模块间的耦合能够清晰可见。 ▪ 利用信息隐蔽,确保每一个模块的独立性。 ▪ 从数据出发去构造程序。 ▪ 不要修补不好的程序,要重新编写。也不要一味地追求代码的复用,要重新组织。 ▪ 对太大的程序,要分块编写、测试,然后再集成。 ▪ 对递归定义的数据结构尽量使用递归过程。 ▪ 注意计算机浮点数运算的特点,例如,浮点数运算 10.0*0.1 通常不等于 1.0 。 ▪ 不要单独进行浮点数的比较。用它们做比较,其结果常常发生异常情况。 ▪ 避免不恰当地追求程序效率,在改进效率前,要做出有关效率的定量估计。 ▪ 在程序中应有出错处理功能,一旦出现故障时不要让机器进行干预,导致停工。 此外,对于程序中的变量,标号,注释等,还需要给予一些注意。例如, ▪ 变量名中尽量不用数字。 ▪ 显式说明所有的变量。 ▪ 确保所有变量在使用前都被初始化。 ▪ 确保注释与代码完全一致。 ▪ 不仅对代码做注释,而且对每条注释都加以编号。 ▪ 不注释不好的代码,要重新编写。 ▪ 程序格式的安排应有助于读者理解程序。 ▪ 注释不要过于繁琐。 ▪ 遵循国家标准。 ▪ 经常反躬自省:“如果我不是编码的人,我能看懂它吗?”考虑它的可理解性达到什 么程度。 (4) 输入和输出 (I / O) 输入和输出信息是与用户的使用直接相关的。输入和输出的方式和格式应当尽可能方便 用户的使用。因此,在软件需求分析阶段和设计阶段,就应基本确定输入和输出的风格。系 统能否被用户接受,有时就取决于输入和输出的风格。 不论是批处理的输入/输出方式,还是交互式的输入/输出方式,在设计和程序编码时 都应考虑下列原则:
对所有的输入数据都进行检验,从而识别错误的输入,以保证每个数据的有效性 检查输入项的各种重要组合的合理性,必要时报告输入状态信息: 使得输入的步骤和操作尽可能简单,并保持简单的输入格式 输入数据时,应允许使用自由格式输入 应允许缺省值 输入一批数据时,最好使用输入结束标志,而不要由用户指定输入数据数目 在以交互式输入/输出方式进行输入时,要在屏幕上使用提示符明确提示交互输入 的请求,指明可使用选择项的种类和取值范围。同时,在数据输入的过程中和输入结束时, 也要在屏幕上给出状态信息: ■当程序设计语言对输入/输出格式有严格要求时,应保持输入格式与输入语句的要 求的一致性 给所有的输出加注解,并设计输出报表格式。 输入/输出风格还受到许多其它因素的影响。如输入/输出设备(例如终端的类型,图 形设备,数字化转换设备等)、用户的熟练程度、以及通信环境等 Wasserman为“用户软件工程及交互系统的设计”提供了一组指导性原则,可供软件设 和编程参考。 把计算机系统的内部特性隐蔽起来不让用户看到: ·有完备的输入出错检査和出错恢复措施,在程序执行过程中尽量排除由于用户的原因 而造成程序出错的可能性; 如果用户的请求有了结果,应随时通知用户 充分利用联机帮助手段,对于不熟练的用户,提供对话式服务,对于熟练的用户,提 供较高级的系统服务,改善输入/输出的能力 ·使输入格式和操作要求与用户的技术水平相适应。对于不熟练的用户,充分利用菜单 系统逐步引导用户操作;对于熟练的用户,允许绕过菜单,直接使用命令方式进行操作 按照输出设备的速度设计信息输出过程 区别不同类型的用户,分别进行设计和编码 保持始终如一的响应时间; 在出现错误时应尽量减少用户的额外工作 在交互式系统中,这些要求应成为软件需求的一部分,并通过设计和编码,在用户和系 统之间建立良好的通信接口。 3.程序效率 (1)讨论效率的准则 程序的效率是指程序的执行速度及程序所需占用的内存的存储空间。讨论程序效率的几 条准则为 效率是一个性能要求,应当在需求分析阶段给出。软件效率以需求为准,不应以人力 所及为准。 好的设计可以提高效率 程序的效率与程序的简单性相关。 般说来,任何对效率无重要改善,且对程序的简单性、可读性和正确性不利的程序设 计方法都是不可取的 (2)算法对效率的影响 源程序的效率与详细设计阶段确定的算法的效率直接有关。在详细设计翻译转换成源程 序代码后,算法效率反映为程序的执行速度和存储容量的要求
6 ▪ 对所有的输入数据都进行检验,从而识别错误的输入,以保证每个数据的有效性; ▪ 检查输入项的各种重要组合的合理性,必要时报告输入状态信息; ▪ 使得输入的步骤和操作尽可能简单,并保持简单的输入格式; ▪ 输入数据时,应允许使用自由格式输入; ▪ 应允许缺省值; ▪ 输入一批数据时,最好使用输入结束标志,而不要由用户指定输入数据数目; ▪ 在以交互式输入/输出方式进行输入时, 要在屏幕上使用提示符明确提示交互输入 的请求,指明可使用选择项的种类和取值范围。同时,在数据输入的过程中和输入结束时, 也要在屏幕上给出状态信息; ▪ 当程序设计语言对输入/输出格式有严格要求时, 应保持输入格式与输入语句的要 求的一致性; ▪ 给所有的输出加注解,并设计输出报表格式。 输入/输出风格还受到许多其它因素的影响。如输入/输出设备(例如终端的类型,图 形设备,数字化转换设备等)、用户的熟练程度、以及通信环境等。 Wasserman 为“用户软件工程及交互系统的设计”提供了一组指导性原则,可供软件设 计和编程参考。 ▪ 把计算机系统的内部特性隐蔽起来不让用户看到; ▪ 有完备的输入出错检查和出错恢复措施,在程序执行过程中尽量排除由于用户的原因 而造成程序出错的可能性; ▪ 如果用户的请求有了结果,应随时通知用户; ▪ 充分利用联机帮助手段,对于不熟练的用户,提供对话式服务,对于熟练的用户,提 供较高级的系统服务,改善输入/输出的能力; ▪ 使输入格式和操作要求与用户的技术水平相适应。对于不熟练的用户,充分利用菜单 系统逐步引导用户操作;对于熟练的用户,允许绕过菜单,直接使用命令方式进行操作; ▪ 按照输出设备的速度设计信息输出过程; ▪ 区别不同类型的用户,分别进行设计和编码; ▪ 保持始终如一的响应时间; ▪ 在出现错误时应尽量减少用户的额外工作。 在交互式系统中,这些要求应成为软件需求的一部分,并通过设计和编码,在用户和系 统之间建立良好的通信接口。 3. 程序效率 (1) 讨论效率的准则 程序的效率是指程序的执行速度及程序所需占用的内存的存储空间。讨论程序效率的几 条准则为: ▪ 效率是一个性能要求,应当在需求分析阶段给出。软件效率以需求为准,不应以人力 所及为准。 ▪ 好的设计可以提高效率。 ▪ 程序的效率与程序的简单性相关。 一般说来,任何对效率无重要改善,且对程序的简单性、可读性和正确性不利的程序设 计方法都是不可取的。 (2) 算法对效率的影响 源程序的效率与详细设计阶段确定的算法的效率直接有关。在详细设计翻译转换成源程 序代码后,算法效率反映为程序的执行速度和存储容量的要求
转换过程中的指导原则是: 在编程序前,尽可能化简有关的算术表达式和逻辑表达式; 仔细检査算法中的嵌套的循环,尽可能将某些语句或表达式移到循环外面: 尽量避免使用多维数组 尽量避免使用指针和复杂的表; 采用“快速”的算术运算 不要混淆数据类型,避免在表达式中出现类型混杂 尽量采用整数算术表达式和布尔表达式; 选用等效的高效率算法; 许多编译程序具有“优化”功能,可以自动生成高效率的目标代码。它可剔除重复的表 达式计算,采用循环求值法、快速的算术运算,以及采用一些能够提高目标代码运行效率的 算法来提高效率。对于效率至上的应用来说,这样的编译程序是很有效的。 (3)影响存储效率的因素 在大中型计算机系统中,存储限制不再是主要问题。在这种环境下,对内存采取基于操 作系统的分页功能的虚拟存储管理,给软件提供了巨大的逻辑地址空间。这时,存储效率与 操作系统的分页功能直接有关,并不是指要使所使用的存储空间达到最少。 釆用结构化程序设计,将程序功能合理分块,使每个模块或一组密切相关模块的程序体 积大小与每页的容量相匹配,可减少页面调度,减少内外存交换,提高存储效率 在微型计算机系统中,存储容量对软件设计和编码的制约很大。因此要选择可生成较短 目标代码且存储压缩性能优良的编译程序,有时需采用汇编程序。通过程序员富有创造性的 努力,提高软件时间与空间效率。 提高存储效率的关键是程序的简单性 (4)影响输入/输出的因素 输入/输出可分为两种类型:一种是面向人(操作员)的输入/输出;一种是面向设备 的输入/输出。如果操作员能够十分方便、简单地录入输入数据,或者能够十分直观、一目 了然地了解输出信息,则可以说面向人的输入/输出是高效的。至于面向设备的输入/输出, 分析起来比较复杂。从详细设计和程序编码的角度来说,可以提出一些提高输入/输出效率 的指导原则 输入/输出的请求应当最小化 对于所有的输入/输出操作,安排适当的缓冲区,以减少频繁的信息交换 对辅助存储(例如磁盘),选择尽可能简单的,可接受的存取方法 对辅助存储的输入/输出,应当成块传送 对终端或打印机的输入/输出,应考虑设备特性,尽可能改善输入/输出的质量和 任何不易理解的,对改善输入/输出效果关系不大的措施都是不可取的 任何不易理解的所谓“超高效”的输入/输出是毫无价值的: 好的输入/输出程序设计风格对提高输入/输出效率会有明显的效果。 4.程序设计语言 程序编码阶段的任务是将软件的详细设计转换成用程序设计语言实现的程序代码。因 此,程序设计语言的性能和设计风格对于程序设计的效能和质量有着直接的关系。 (1)程序设计语言特性的比较 ①软件心理学的观点 因为从设计到编码的转换基本上是人的活动,因此语言的性能对程序员的心理影响,将
7 转换过程中的指导原则是: ▪ 在编程序前,尽可能化简有关的算术表达式和逻辑表达式; ▪ 仔细检查算法中的嵌套的循环,尽可能将某些语句或表达式移到循环外面; ▪ 尽量避免使用多维数组; ▪ 尽量避免使用指针和复杂的表; ▪ 采用“快速”的算术运算; ▪ 不要混淆数据类型,避免在表达式中出现类型混杂; ▪ 尽量采用整数算术表达式和布尔表达式; ▪ 选用等效的高效率算法; 许多编译程序具有“优化”功能,可以自动生成高效率的目标代码。它可剔除重复的表 达式计算,采用循环求值法、快速的算术运算,以及采用一些能够提高目标代码运行效率的 算法来提高效率。对于效率至上的应用来说,这样的编译程序是很有效的。 (3) 影响存储效率的因素 在大中型计算机系统中,存储限制不再是主要问题。在这种环境下,对内存采取基于操 作系统的分页功能的虚拟存储管理,给软件提供了巨大的逻辑地址空间。这时,存储效率与 操作系统的分页功能直接有关,并不是指要使所使用的存储空间达到最少。 采用结构化程序设计,将程序功能合理分块,使每个模块或一组密切相关模块的程序体 积大小与每页的容量相匹配,可减少页面调度,减少内外存交换,提高存储效率。 在微型计算机系统中,存储容量对软件设计和编码的制约很大。因此要选择可生成较短 目标代码且存储压缩性能优良的编译程序,有时需采用汇编程序。通过程序员富有创造性的 努力,提高软件时间与空间效率。 提高存储效率的关键是程序的简单性。 (4) 影响输入/输出的因素 输入/输出可分为两种类型:一种是面向人(操作员)的输入/输出;一种是面向设备 的输入/输出。如果操作员能够十分方便、简单地录入输入数据,或者能够十分直观、一目 了然地了解输出信息,则可以说面向人的输入/输出是高效的。至于面向设备的输入/输出, 分析起来比较复杂。从详细设计和程序编码的角度来说,可以提出一些提高输入/输出效率 的指导原则: ▪ 输入/输出的请求应当最小化; ▪ 对于所有的输入/输出操作,安排适当的缓冲区,以减少频繁的信息交换。 ▪ 对辅助存储(例如磁盘),选择尽可能简单的,可接受的存取方法; ▪ 对辅助存储的输入/输出,应当成块传送; ▪ 对终端或打印机的输入/输出,应考虑设备特性, 尽可能改善输入/输出的质量和 速度; ▪ 任何不易理解的,对改善输入/输出效果关系不大的措施都是不可取的; ▪ 任何不易理解的所谓“超高效”的输入/输出是毫无价值的; ▪ 好的输入/输出程序设计风格对提高输入/输出效率会有明显的效果。 4. 程序设计语言 程序编码阶段的任务是将软件的详细设计转换成用程序设计语言实现的程序代码。因 此,程序设计语言的性能和设计风格对于程序设计的效能和质量有着直接的关系。 (1) 程序设计语言特性的比较 ① 软件心理学的观点 因为从设计到编码的转换基本上是人的活动,因此语言的性能对程序员的心理影响,将
对转换产生重大影响。程序员总是希望选择简单易学、使用方便的语言,以减少程序出错率, 提高软件可靠性。从心理学的观点,影响程序员心理的语言特性有如下六种: 一致性:它表示一种语言所使用符号的兼容程度、允许随意规定限制、以及允许对 法或语义破例的程度。同是一个符号,给予多种用途,会引起许多难以察觉的错误 二义性:虽然语言的编译程序总是以一种机械的规则来解释语句,但读者则可能用 不同的方式来理解语句。例如,对于一个逻辑表达式A≥“0”andA≤“9”,读者可能对 这个逻辑表达式有不同的理解 如果一个程序设计语言缺乏一致性和存在二义性,那么用这种语言编写出来的程序可读 性就差,同时用这种语言编程也容易出错 简洁性(紧凑性):表示程序员为了用该语言编写程序,必须记忆的有关编码的信息 量。可用语言支持块结构和结构化程序的能力、可使用的保留字和缩写字的种类、数据类型 的种类和缺省说明、算术运算符和逻辑运算符的种类、系统内标准函数的数目等来衡量 遗憾的是,语言的简洁性与程序的一致性常常是抵触的。 局部性:指程序设计语言的联想(综合)特性。综合的特性使人们能够对事物从整 体上进行记忆和识别。在编码过程中,由语句组合成模块,由模块组装为程序体系结构,并 在组装过程中实现模块的高内聚和低耦合,可使程序的局部性加强 线性:指程序的联想(顺序)特性。人们总是习惯于按逻辑线性序列理解程序。如 果程序中线性序列和逻辑运算较多,会提高可读性。如果存在大量的分支和循环,就会破坏 顺序状态,增加理解上的困难。直接实现结构化程序可提高程序的线性特性 传统:人们学习一种新的程序设计语言的能力受到传统的影响。具有 Pascal基础的 程序人员在学习C语言时不会感到困难,因为C保持了 Pasca所确立的传统语言特性。但是 要求同一个人去学习APL或者LISP这样一些语言,传统就中断了。 ②软件工程的观点 从软件工程观点,程序设计语言的特性应着重考虑软件开发项目的需要。为此,对于程 序编码,有如下一些工程上的性能要求 ■详细设计应能直接地容易地翻译成代码程序:把设计变为程序的难易程度,反映了 程序设计语言与设计说明相接近的程度。所选择的程序设计语言是否具有结构化的构造,复 杂的数据结构,专门的输入/输出能力,位运算和串处理的能力,直接影响到从详细设计变 换到代码程序的难易程度,以及特定软件开发项目的可实现性。 源程序应具有可移植性:源程序的可移植性通常有三种解释:①对源程序不做修改 或少做修改就可以实现处理机上的移植或编译程序上的移植:②即使程序的运行环境改变 (例如,改用一个新版本的操作系统),源程序也不用改变:③源程序的许多模块可以不做 修改或少做修改就能集成为功能性的各种软件包,以适应不同的需要。 为改善软件的可移植性,主要是使语言标准化。在开发软件时,应严格地遵守ISO或 ANSI、GB的标准,而不要去理会特定编译器提供的非标准特性 编译程序应具有较高的效率 尽可能应用代码生成的自动工具:有效的软件开发工具是缩短编码时间,改善源代 码质量的关键因素。使用带有各种有效的自动化工具的“软件开发环境”,支持从设计到源代 码的翻译等各项工作,可以保证软件开发获得成功。 可维护性:源程序的可读性,语言自身的文档化特性(涉及标识符的允许长度、标 号命名、数据类型的丰富程度、控制结构的规定等)是影响到可维护性的重要因素。 ③程序设计语言的技术性能 在计划阶段,极少考虑程序语言的技术特性。但在选定资源时,要规划将要使用的支撑 工具,就要确定一个具体的编译器或者确定一个程序设计环境。如果软件开发组的成员对所
8 对转换产生重大影响。程序员总是希望选择简单易学、使用方便的语言,以减少程序出错率, 提高软件可靠性。从心理学的观点,影响程序员心理的语言特性有如下六种: ▪ 一致性 :它表示一种语言所使用符号的兼容程度、允许随意规定限制、以及允许对 语法或语义破例的程度。同是一个符号,给予多种用途,会引起许多难以察觉的错误。 ▪ 二义性 :虽然语言的编译程序总是以一种机械的规则来解释语句,但读者则可能用 不同的方式来理解语句。例如,对于一个逻辑表达式 A ≥“0”and A ≤“9”,读者可能对 这个逻辑表达式有不同的理解。 如果一个程序设计语言缺乏一致性和存在二义性,那么用这种语言编写出来的程序可读 性就差,同时用这种语言编程也容易出错。 ▪ 简洁性(紧凑性):表示程序员为了用该语言编写程序,必须记忆的有关编码的信息 量。可用语言支持块结构和结构化程序的能力、可使用的保留字和缩写字的种类、数据类型 的种类和缺省说明、算术运算符和逻辑运算符的种类、系统内标准函数的数目等来衡量。 遗憾的是,语言的简洁性与程序的一致性常常是抵触的。 ▪ 局部性 :指程序设计语言的联想(综合)特性。综合的特性使人们能够对事物从整 体上进行记忆和识别。在编码过程中,由语句组合成模块,由模块组装为程序体系结构,并 在组装过程中实现模块的高内聚和低耦合,可使程序的局部性加强。 ▪ 线性 :指程序的联想(顺序)特性。人们总是习惯于按逻辑线性序列理解程序。如 果程序中线性序列和逻辑运算较多,会提高可读性。如果存在大量的分支和循环,就会破坏 顺序状态,增加理解上的困难。直接实现结构化程序可提高程序的线性特性。 ▪ 传统 :人们学习一种新的程序设计语言的能力受到传统的影响。具有 Pascal 基础的 程序人员在学习 C 语言时不会感到困难,因为 C 保持了 Pascal 所确立的传统语言特性。但是 要求同一个人去学习 APL 或者 LISP 这样一些语言,传统就中断了。 ② 软件工程的观点 从软件工程观点,程序设计语言的特性应着重考虑软件开发项目的需要。为此,对于程 序编码,有如下一些工程上的性能要求: ▪ 详细设计应能直接地容易地翻译成代码程序 :把设计变为程序的难易程度,反映了 程序设计语言与设计说明相接近的程度。所选择的程序设计语言是否具有结构化的构造,复 杂的数据结构,专门的输入/输出能力,位运算和串处理的能力,直接影响到从详细设计变 换到代码程序的难易程度,以及特定软件开发项目的可实现性。 ▪ 源程序应具有可移植性 :源程序的可移植性通常有三种解释:① 对源程序不做修改 或少做修改就可以实现处理机上的移植或编译程序上的移植;② 即使程序的运行环境改变 (例如,改用一个新版本的操作系统),源程序也不用改变;③ 源程序的许多模块可以不做 修改或少做修改就能集成为功能性的各种软件包,以适应不同的需要。 为改善软件的可移植性,主要是使语言标准化。在开发软件时,应严格地遵守 ISO 或 ANSI、GB 的标准,而不要去理会特定编译器提供的非标准特性。 ▪ 编译程序应具有较高的效率。 ▪ 尽可能应用代码生成的自动工具 :有效的软件开发工具是缩短编码时间,改善源代 码质量的关键因素。使用带有各种有效的自动化工具的“软件开发环境”,支持从设计到源代 码的翻译等各项工作,可以保证软件开发获得成功。 ▪ 可维护性 :源程序的可读性,语言自身的文档化特性(涉及标识符的允许长度、标 号命名、数据类型的丰富程度、控制结构的规定等)是影响到可维护性的重要因素。 ③ 程序设计语言的技术性能 在计划阶段,极少考虑程序语言的技术特性。但在选定资源时,要规划将要使用的支撑 工具,就要确定一个具体的编译器或者确定一个程序设计环境。如果软件开发组的成员对所
要使用的语言不熟悉,那么在成本及进度估算时必须把学习的工作量估算在内。 旦确定了软件需求,待选用的程序语言的技术特性就显得非常重要了。如果需要复杂 的数据结构,就要仔细衡量有哪些语言能提供这些复杂的数据结构。如果首要的是高性能及 实时处理的能力,就可选用适合于实时应用的语言或效率高的语言。如果该应用有许多输出 报告或繁杂的文件处理,最好是根据软件的要求,选定一种适合于该项工作的语言。 软件的设计质量与程序设计语言的技术性能无关(面向对象设计例外)。但在实现软件 设计转化为程序代码时,转化的质量往往受语言性能的影响。因而也会影响到设计方法 语言的技术性能对测试和维护的影响是多种多样的。例如,直接提供结构化构造的语言 有利于减少循环带来的复杂性(即 Mccabe复杂性),使程序易读、易测试、易维护。另一方 面,语言的某些技术特性却会妨碍测试。例如,在面向对象的语言程序中,由于实行了数据 封装,使得监控这些数据的执行状态变得比较困难:由于建立了对象类的继承结构,使得高 内聚、低耦合的要求受到破坏,增加了测试的困难。此外,只要语言程序的可读性强,而且 可以减少程序的复杂性,这样的程序设计语言对于软件的维护就是有利的 总之,通过仔细地分析和比较,选择一种功能强而又适用的语言,对成功地实现从软件 设计到编码的转换,提高软件的质量,改善软件的可测试性和可维护性是至关重要的。 (2)程序设计语言的分类 目前,用于软件开发的程序设计语言已经有数百种之多,对这些程序设计语言的分类有 不少争议。同一种语言可以归到不同的类中。从软件工程的角度,根据程序设计语言发展的 历程,可以把它们大致分为4类。 ①从属于机器的语言(第一代语言):它是由机器指令代码组成的语言。对于不同的机 器就有相应的一套机器语言。用这种语言编写的程序,都是二进制代码的形式,且所有的地 址分配都是以绝对地址的形式处理。存储空间的安排,寄存器、变址的使用都由程序员自己 计划。因此使用机器语言编写的程序很不直观,在计算机内的运行效率很高但编写出的机器 语言程序其出错率也高。 ②汇编语言(第二代语言):汇编语言比机器语言直观,它的每一条符号指令与相应的 机器指令有对应关系,同时又增加了一些诸如宏、符号地址等功能。存储空间的安排可由机 器解决。不同指令集的处理器系统就有自己相应的汇编语言。从软件工程的角度来看,汇编 语言只是在高级语言无法满足设计要求时,或者不具备支持某种特定功能(例如特殊的输入 /输出)的技术性能时,才被使用。 ③高级程序设计语言(第三代语言) 传统的高级程序设计语言:如 FORTRAN、 COBOL、 ALGOL、 BASIC等。这些程序 语言曾得到广泛应用。目前,它们都已有多种版本。有的语言得到较大的改进,甚至形成了 可视的开发环境,具有图形设计工具、结构化的事件驱动编程模式、开放的环境,使用户可 以既快又简便地编制出 windows下的各种应用程序。 通用的结构化程序设计语言:它具有很强的过程功能和数据结构功能,并提供结构 化的逻辑构造。这一类语言的代表是PL1, PASCAL,C和Ada。此外, COBOL78、 Turbo BASIC 等也应归入第三代程序语言范围。 ■专用语言:专用语言是为特殊的应用而设计的语言。通常具有自己特殊的语法形式, 面对特定的问题,输入结构及词汇表与该问题的相应范围密切相关。有代表性的专用语言有 APL、Lisp、 PROLOG、 Smalltalk丶C++、 FORTE等。从软件工程的角度来看,专用语言支 寺了特殊的应用,将特定的设计要求翻译成可执行的代码。但是它们的可移植性和可维护性 比较差。 ④第四代语言(4GL):4GL用不同的文法表示程序结构和数据结构,但是它是在更高 一级抽象的层次上表示这些结构,它不再需要规定算法的细节。4GL兼有过程性和非过程性
9 要使用的语言不熟悉,那么在成本及进度估算时必须把学习的工作量估算在内。 一旦确定了软件需求,待选用的程序语言的技术特性就显得非常重要了。如果需要复杂 的数据结构,就要仔细衡量有哪些语言能提供这些复杂的数据结构。如果首要的是高性能及 实时处理的能力,就可选用适合于实时应用的语言或效率高的语言。如果该应用有许多输出 报告或繁杂的文件处理,最好是根据软件的要求,选定一种适合于该项工作的语言。 软件的设计质量与程序设计语言的技术性能无关(面向对象设计例外)。但在实现软件 设计转化为程序代码时,转化的质量往往受语言性能的影响。因而也会影响到设计方法。 语言的技术性能对测试和维护的影响是多种多样的。例如,直接提供结构化构造的语言 有利于减少循环带来的复杂性(即 McCabe 复杂性),使程序易读、易测试、易维护。另一方 面,语言的某些技术特性却会妨碍测试。例如,在面向对象的语言程序中,由于实行了数据 封装,使得监控这些数据的执行状态变得比较困难;由于建立了对象类的继承结构,使得高 内聚、低耦合的要求受到破坏,增加了测试的困难。此外,只要语言程序的可读性强,而且 可以减少程序的复杂性,这样的程序设计语言对于软件的维护就是有利的。 总之,通过仔细地分析和比较,选择一种功能强而又适用的语言,对成功地实现从软件 设计到编码的转换,提高软件的质量,改善软件的可测试性和可维护性是至关重要的。 (2) 程序设计语言的分类 目前,用于软件开发的程序设计语言已经有数百种之多,对这些程序设计语言的分类有 不少争议。同一种语言可以归到不同的类中。从软件工程的角度,根据程序设计语言发展的 历程,可以把它们大致分为 4 类。 ① 从属于机器的语言(第一代语言):它是由机器指令代码组成的语言。对于不同的机 器就有相应的一套机器语言。用这种语言编写的程序,都是二进制代码的形式,且所有的地 址分配都是以绝对地址的形式处理。存储空间的安排,寄存器、变址的使用都由程序员自己 计划。因此使用机器语言编写的程序很不直观,在计算机内的运行效率很高但编写出的机器 语言程序其出错率也高。 ② 汇编语言(第二代语言):汇编语言比机器语言直观,它的每一条符号指令与相应的 机器指令有对应关系,同时又增加了一些诸如宏、符号地址等功能。存储空间的安排可由机 器解决。不同指令集的处理器系统就有自己相应的汇编语言。从软件工程的角度来看,汇编 语言只是在高级语言无法满足设计要求时,或者不具备支持某种特定功能(例如特殊的输入 /输出)的技术性能时,才被使用。 ③ 高级程序设计语言(第三代语言) ▪ 传统的高级程序设计语言 :如 FORTRAN、COBOL、ALGOL、BASIC 等。这些程序 语言曾得到广泛应用。目前,它们都已有多种版本。有的语言得到较大的改进,甚至形成了 可视的开发环境,具有图形设计工具、结构化的事件驱动编程模式、开放的环境,使用户可 以既快又简便地编制出 windows 下的各种应用程序。 ▪ 通用的结构化程序设计语言 :它具有很强的过程功能和数据结构功能,并提供结构 化的逻辑构造。这一类语言的代表是 PL/1,PASCAL,C 和 Ada。此外,COBOL78、Turbo BASIC 等也应归入第三代程序语言范围。 ▪ 专用语言 :专用语言是为特殊的应用而设计的语言。通常具有自己特殊的语法形式, 面对特定的问题,输入结构及词汇表与该问题的相应范围密切相关。有代表性的专用语言有 APL、Lisp、PROLOG、Smalltalk、C++、FORTH 等。从软件工程的角度来看,专用语言支 持了特殊的应用,将特定的设计要求翻译成可执行的代码。但是它们的可移植性和可维护性 比较差。 ④ 第四代语言(4GL):4GL 用不同的文法表示程序结构和数据结构,但是它是在更高 一级抽象的层次上表示这些结构,它不再需要规定算法的细节。4GL 兼有过程性和非过程性
的两重特性。程序员规定条件和相应的动作这是过程性的部分,并且指出想要的结果,这是 非过程部分。然后由4GL语言系统运用它的专门领域的知识来填充过程细节 Martin把第四代语言分为以下几种类型: 查询语言:用户可利用查询语言对预先定义在数据库中的信息进行较复杂的操作。 ■程序生成器:只需很少的语句就能生成完整的第三代语言程序,它不必依赖预先定 义的数据库作为它的着手点 其它4GL:如判定支持语言、原型语言、形式化规格说明语言等。 (3)程序设计语言的选择 为某个特定开发项目选择程序设计语言时,既要从技术角度、工程角度、心理学角度评 价和比较各种语言的适用程度,又必须考虑现实可能性。有时需要作出某种合理的折衷 在选择与评价语言时,首先要从问题入手,确定它的要求是什么?这些要求的相对重要 性如何?再根据这些要求和相对重要性来衡量能采用的语言 通常考虑的因素有①项目的应用范围:②算法和计算复杂性;③软件执行的环境 ④性能上的考虑与实现的条件:⑤数据结构的复杂性:⑥软件开发人员的知识水平和心理 因素等。其中,项目的应用范围是最关键的因素 针对计算机的4个主要应用领域,为语言做一个粗略的分类。例如,在科学与工程计算 领域内,C,C++语言得到了广泛的应用,但 FORTRAN仍然是应用最广泛的语言。在商业 数据处理领域中,通常采用 COBOL,RPG语言编写程序,当然也可选用SQL语言或其它专 用语言。在系统程序设计和实时应用领域中,汇编语言或一些新的派生语言,如BLSS,PL /S,Ada,C++等得到了广泛的应用。在人工智能领域以及问题求解,组合应用领域,主要 采用LSP和 PROLOG语言。 新的更强有力的语言,虽然对于应用有很强的吸引力,但是因为已有的语言已经积累了 大量的久经使用的程序,具有完整的资料、支撑软件和软件开发工具,程序设计人员比较熟 悉,而且有过类似项目的开发经验和成功的先例,由于心理因素,人们往往宁愿选用原有的 语种。所以应当彻底地分析,评价,介绍新的语言,以便从原有语言过渡到新的语言。 5.程序复杂性度量 程序复杂性主要指模块内程序的复杂性。它直接关联到软件开发费用的多少,开发周期 的长短和软件内部潜伏错误的多少。同时它也是软件可理解性的另一种度量。 减少程序复杂性,可提高软件的简单性和可理解性,并使软件开发费用减少,开发周期 缩短,软件内部潜藏错误减少 (1)代码行度量法 度量程序的复杂性,最简单的方法就是统计程序的源代码行数。此方法基于两个前提 ·程序复杂性随着程序规模的増加不均衡地增长 ·控制程序规模的方法最好是采用分而治之的办法。将一个大程序分解成若干个简单的 可理解的程序段。 方法的基本考虑是统计一个程序模块的源代码行数目,并以源代码行数做为程序复杂性 的度量。若设每行代码的出错率为每100行源程序中可能有的错误数目,例如每行代码的出 错率为1%,则是指每100行源程序中可能有一个错误。 Thayer曾指出,程序出错率的估算范围是从0.04%~7%之间,即每100行源程序中可 能存在0.04~7个错误。他还指出,每行代码的出错率与源程序行数之间不存在简单的线性 关系。 Lipow进一步指出,对于小程序,每行代码的出错率为1.3%~1.8%;对于大程序, 每行代码的出错率增加到27%~3.2%之间,但这只是考虑了程序的可执行部分,没有包括 程序中的说明部分。 Lipow及其他研究者得出一个结论:对于少于100个语句的小程序,源
10 的两重特性。程序员规定条件和相应的动作这是过程性的部分,并且指出想要的结果,这是 非过程部分。然后由 4GL 语言系统运用它的专门领域的知识来填充过程细节。 Martin 把第四代语言分为以下几种类型: ▪ 查询语言 :用户可利用查询语言对预先定义在数据库中的信息进行较复杂的操作。 ▪ 程序生成器 :只需很少的语句就能生成完整的第三代语言程序,它不必依赖预先定 义的数据库作为它的着手点。 ▪ 其它 4GL :如判定支持语言、原型语言、形式化规格说明语言等。 (3) 程序设计语言的选择 为某个特定开发项目选择程序设计语言时,既要从技术角度、工程角度、心理学角度评 价和比较各种语言的适用程度,又必须考虑现实可能性。有时需要作出某种合理的折衷。 在选择与评价语言时,首先要从问题入手,确定它的要求是什么? 这些要求的相对重要 性如何? 再根据这些要求和相对重要性来衡量能采用的语言。 通常考虑的因素有 ① 项目的应用范围;② 算法和计算复杂性;③ 软件执行的环境; ④ 性能上的考虑与实现的条件;⑤ 数据结构的复杂性;⑥ 软件开发人员的知识水平和心理 因素等。其中,项目的应用范围是最关键的因素。 针对计算机的 4 个主要应用领域,为语言做一个粗略的分类。例如,在科学与工程计算 领域内,C,C++ 语言得到了广泛的应用,但 FORTRAN 仍然是应用最广泛的语言。在商业 数据处理领域中,通常采用 COBOL,RPG 语言编写程序,当然也可选用 SQL 语言或其它专 用语言。在系统程序设计和实时应用领域中,汇编语言或一些新的派生语言,如 BLISS,PL /S,Ada,C++等得到了广泛的应用。在人工智能领域以及问题求解,组合应用领域,主要 采用 LISP 和 PROLOG 语言。 新的更强有力的语言,虽然对于应用有很强的吸引力,但是因为已有的语言已经积累了 大量的久经使用的程序,具有完整的资料、支撑软件和软件开发工具,程序设计人员比较熟 悉,而且有过类似项目的开发经验和成功的先例,由于心理因素,人们往往宁愿选用原有的 语种。所以应当彻底地分析,评价,介绍新的语言,以便从原有语言过渡到新的语言。 5. 程序复杂性度量 程序复杂性主要指模块内程序的复杂性。它直接关联到软件开发费用的多少,开发周期 的长短和软件内部潜伏错误的多少。同时它也是软件可理解性的另一种度量。 减少程序复杂性,可提高软件的简单性和可理解性,并使软件开发费用减少,开发周期 缩短,软件内部潜藏错误减少。 (1) 代码行度量法 度量程序的复杂性,最简单的方法就是统计程序的源代码行数。此方法基于两个前提: ·程序复杂性随着程序规模的增加不均衡地增长; ·控制程序规模的方法最好是采用分而治之的办法。将一个大程序分解成若干个简单的 可理解的程序段。 方法的基本考虑是统计一个程序模块的源代码行数目,并以源代码行数做为程序复杂性 的度量。若设每行代码的出错率为每 100 行源程序中可能有的错误数目, 例如每行代码的出 错率为 1%,则是指每 100 行源程序中可能有一个错误。 Thayer 曾指出,程序出错率的估算范围是从 0.04%~7%之间,即每 100 行源程序中可 能存在 0.04~7 个错误。他还指出,每行代码的出错率与源程序行数之间不存在简单的线性 关系。Lipow 进一步指出,对于小程序,每行代码的出错率为 1.3%~1.8%; 对于大程序, 每行代码的出错率增加到 2.7%~3.2%之间,但这只是考虑了程序的可执行部分,没有包括 程序中的说明部分。Lipow 及其他研究者得出一个结论:对于少于 100 个语句的小程序,源