第六章 系统实施.docx
《第六章 系统实施.docx》由会员分享,可在线阅读,更多相关《第六章 系统实施.docx(77页珍藏版)》请在冰点文库上搜索。
![第六章 系统实施.docx](https://file1.bingdoc.com/fileroot1/2023-6/28/aa679cd1-75d1-4f93-b890-4497be75de99/aa679cd1-75d1-4f93-b890-4497be75de991.gif)
第六章系统实施
第六章系统实施、维护与评价
系统实施是指将系统设计阶段的结果在计算机上实现。
将原来纸面上的、类似于设计图式的新系统方案转换为可执行的应用软件系统。
系统实施的主要内容包括购置和安装硬件设备、程序设计(购买)与调试、系统操作人员的培训、系统有关数据的准备与录入、系统调试和转换。
系统维护是为了应付信息系统的环境和其他因素的各种变化,保证系统正常工作而采取的一切活动,它包括系统功能的改进和解决在系统运行期间发生的一切问题和错误。
系统评价是指系统建成后,经过一段时间的运行,对系统目标与功能的实现情况进行检查,并与系统开发中设立的系统预期目标进行对比,及时写出系统评价报告。
6.1 程序设计
目前,人和计算机通信仍然必须使用人工设计的语言,也即是程序设计语言。
所谓程序设计就是把软件设计的结果翻译成计算机可以“理解”的形式----用某种程序设计语言书写的程序。
6.1.1 程序设计语言
程序设计的目的是实现人和计算机的通信,指挥计算机按人的意志正确工作。
程序设计语言是人和计算机通信的最基本的工具,程序设计语言的特性不可避免地会影响人思维和解决问题的方式,会影响人和计算机通信的方式和质量,也会影响其他人阅读和理解程序的难易程度。
因此,程序设计之前的一项重要工作就是选择一种适当的程序设计语言。
本节将从软件工程的角度,简单讨论几个和程序设计语言有关的问题。
一、程序设计语言分类
据文献报导,自六十年代以来人们已经设计和实现了数千种不同的程序设计语言,但是只有其中很少一部分得到了比较广泛的应用。
现有的程序设计语言虽然五花八门、品种繁多,但它们基本上可以分为汇编语言和高级语言(包括超高级语言)两大类。
汇编语言的语句和计算机硬件操作有一一对应关系,每种汇编语言都是支持这种语言的计算机所独有的,因此,基本上有多少种计算机也就有多少种汇编语言。
高级语言使用的概念和符号与人们通常使用的概念和符号比较接近,它的一个语句往往对应若干条机器指令,一般地说,高级语言的特性不依赖于实现这种语言的计算机。
对于高级语言还应该进一步分类,以加深对它们的了解。
我们可以分别从应用特点或语言内在特点两个不同角度对高级语言进行分类。
从应用特点看,高级语言可以分为基础语言、结构化语言和专用语言三类。
基础语言是通用语言,它们的特点是历史悠久、应用广泛,有大量软件库,为最广泛的人所熟悉和接受。
属于这类语言的有BASIC、FORTRAN、COBOL和ALGOL。
结构化语言也是通用语言。
这类语言的特点是直接提供结构化的控制结构,具有很强的过程能力和数据结构能力。
ALGOL是最早的结构化语言(同时又是基础语言),由它派生出来的PL/1、PASCAL、C等语言正应用在非常广泛的领域中。
专用语言的特点是,具有为某种特殊应用而设计的独特的语法形式。
一般地说,这类语言的应用范围比较狭窄。
例如,APL是为数组和向量运算设计的简洁而又功能很强的语言,然而它几乎不提供结构化的控制结构和数据类型;BLISS是为开发编译程序和操作系统而设计的语言;FORTH是为开发微处理机软件而设计的语言,它的特点是以面向堆栈的方式执行用户定义的函数,因此能提高速度和节省存储;LISP和PROLOG两种语言特别适合于人工智能领域的应用。
从语言的内在特点看,高级语言可以分为系统实现语言、静态高级语言、块结构高级语言和动态高级语言等四类。
系统实现语言是为了克服汇编程序设计的困难而从汇编语言发展起来的。
这类语言提供控制语句和变量类型检验等功能,但是同时也容许程序员直接使用机器操作。
例如,C语言就是著名的系统实现语言。
静态高级语言给程序员提供某些控制语句和变量说明的机制,但是程序员不能直接控制由编译程序生成的机器操作。
这类语言的特点是静态地分配存储。
这种存储分配方法虽然方便了编译程序的设计和实现,但是对使用这类语言的程序员施加了较多限制。
因为这类语言是第一批出现的高级语言,所以使用非常广泛。
COBOL和FORTRAN是这类语言中最著名的例子。
块结构高级语言的特点是提供有限形式的动态存储分配,这种形式称为块结构。
存储管理系统支持程序的运行,每当进入或退出程序块时,存储管理系统分配存储或释放存储。
程序块是程序中界限分明的区域,每当进入一个程序块时就中断程序的执行,以便分配存储。
ALGOL和PASCAL是这类语言的代表。
动态高级语言的特点是动态地完成所有存储管理,也就是说,执行个别语句可能引起分配存储或释放存储。
一般地说,这类语言的结构和静态的或块结构的高级语言的结构都很不相同,实际上这类语言中任何两种语言的结构彼此间也很少类似。
这类语言一般是为特殊应用而设计的,不属于通用语言。
二、程序设计语言的特点
软件工程师应该了解程序设计语言各方面的特点,以及这些特点对软件质量的影响,以便在需要为一个特定的开发项目选择语言时,能作出合理的技术抉择。
下面从几个不同侧面简单讨论程序设计语言的特点。
1、名字说明
预先说明程序中使用的对象的名字,使编译程序能检查程序中出现的名字的合法性,从而能帮助程序员发现和改正程序中的错误。
某些语言(例如,FORTRAN和BASIC)并不要求用户显式地说明程序中所有对象的名字,第一次使用一个名字被看做是对这个名字的说明。
然而在输入源程序时如果拼错了名字,特别是如果错输入的字符和预定要使用的字符非常相像(例如,字母为O数字0,小写字母l数字1),那么因此而造成的错误是较难诊断的。
2、类型说明
类型说明和名字说明是紧密相联的,通过类型说明用户定义了对象的类型,从而确定了该对象的使用方式。
编译程序能够发现程序中对某个特定类型的对象使用不当的错误,因此有助于减少程序错误。
规定必须预先说明对象的类型还有助于减少阅读程序时的歧义性。
某些语言(如FORTRAN)规定,如果不加说明地引入一个名字,那么该名字的第一个字母将决定它的类型。
然而,如果说明了某个名字的类型,而且说明的类型和由名字第一个字母决定的缺省类型不一致,则会造成阅读程序时的歧义性。
类型检查的概念最早是在ALGOL60中引入的,以后又显著地精化了这个概念。
像PASCAL这样的程序设计语言,还允许用户定义与他们的特定应用有关的自己的类型,并且可以再用自己定义的类型说明其他程序对象。
用户既可以定义纯量、子界一类的简单类型,也可以定义记录、链表和二叉树等复杂的结构类型。
程序设计语言中的类型说明不仅仅是一种安全措施,它还是一种重要的抽象机制。
对类型名字的定义使得我们可以引用某些复杂的实体,而不必考虑这些实体的表示方法。
3、初始化
程序设计中最常见的错误之一是在使用变量之前没对变量初始化(即,提供适当的初始值)。
为减少发生错误的可能性,应该强迫程序员对程序中说明的所有变量初始化。
另一个办法是在说明变量时由系统给变量赋一个特殊的表明它尚未初始化的值,以后如果没给这个变量赋值就企图使用它的值,系统就会发出出错信号。
4、程序对象的局部性
程序设计的一般原理是,程序对象的名字应该在靠近使用它们的地方引入,并且应该只有程序中真正需要它们的那些部分才能访问它们。
通常有两种提供局部变量的途径,FORTRAN和绝大多数系统实现语言提供单层局部性,块结构语言提供多层局部性。
如果名字的特性在靠近使用这些名字的地方说明,程序的读者就很容易获得有关这些名字的信息,因此多层次的局部性有助于提高程序的可读性。
此外,具有多层次局部性的语言鼓励程序员尽量使用局部的对象(变量或常量),这不仅有助于提高可读性,而且有助于减少差错和提高程序的可修改性。
但是,在块结构语言中,如果内层模块说明的名字和外层模块中说明的名字相同,则在内层模块中这些外层模块的对象变成不可访问。
当模块多层嵌套时,可能会由于疏忽在内层模块中说明了和外层模块中相同的名字,从而引起差错。
特别在维护阶段,维护人员往往不是原来写程序的人,更容易出现这种差错。
虽然用单层局部性语言写的程序可读性不如多层局部性的,但是却容易实现程序单元的独立编译。
5、程序模块
块结构语言提供了控制程序对象名字可见性的某些手段,主要是在较内层程序块中说明的名字不能被较外层的程序块访问。
此外,由于动态存储分配的缘故,在两次调用一个程序块的间隔中不能保存局部对象的值。
因此,即使是只有一两个子程序使用的对象,如果需要在两次调用这些子程序的过程中保存这个对象的值,也必须把这个对象说明成全程的,也就是程序中所有子程序都可以访问的,然而这将增加维护时发生差错的可能性。
从控制名字的可见性这个角度来说,块结构语言提供的机制是不完善的,需要某种附加的机制,以允许用户指定哪些局部名字可以从说明这些名字的程序块外面访问,还应该能够要求某个局部变量在两次调用包含它的程序块的过程中保存它的值。
目前已经有一些语言可以提供上述这些功能,但是在不同语言中这种控制机制的名称也不同。
6、循环控制结构
最常见的循环控制结构有for语句(循环给定次数),while—do语句(每次进入循环体之前测试循环结束条件)和repeat—until语句(每执行完一次循环体,测试循环结束条件)。
但是,实际上有许多场合需要在循环体内任意一点测试循环结束条件,如果使用if—then—else语句和附加的布尔变量实现这个要求,则将增加程序长度并降低程序的可读性。
某些程序设计语言考虑到了上述要求,并且适当地解决了这个问题,例如,Ada语言提供了exit语句,它的形式是:
exit{<标识符>}{When<条件>}
这个语句把控制转移到循环语句后面的语句,或转移到由该语句中列出的标识符做标号的语句。
7、分支控制结构
一臂(没有else部分)和两臂(有else部分)的if型分支语句,通常并不存在什么实际问题,但是多分支的case型语句却可能存在下述两个问题:
第一,如果case表达式取的值不在预先指定的范围内,则不能决定应该做的动作;第二,在某些程序设计语言中,由case表达式选定执行的语句,取决于所有可能执行的语句的排列次序,如果语句次序排错了(当有虚语句时很容易发生这类错误),编译和运行时系统并不能发现这类错误。
PASCAL语言的case语句,用case表达式的值和case标号匹配的办法,选择应该执行的语句,从而解决了上述第二个问题。
Ada语言的case语句还进一步增加了补缺标号(‘others'),从而也解决了上述的第一个问题。
8、异常处理
程序运行过程中发生的错误或意外事件称为异常。
多数程序设计语言在检测和处理异常方面几乎没给程序员提供任何帮助,程序员只能使用语言提供的一般控制结构检测异常,并在发生异常时把控制转移到处理异常的程序段。
但是,当程序中包含一系列子程序的嵌套调用时,并没有方便而又可靠的方法把出现异常的信息从一个子程序传送到另外的子程序。
使用一般控制结构加布尔变量的方法,需要明显增加程序长度并且使程序的逻辑变得繁琐难懂。
PL/1语言,特别是Ada语言,考虑到了异常处理问题,提供了相应的机制,从而不必为异常处理过分增加程序长度,并且可以把出现异常的信息从一个程序单元方便地传送到另一个单元。
9、独立编译
独立编译意味着能分别编译各个程序单元,然后再把它们集成为一个完整的程序。
典型地,一个大程序由许多不同的程序单元(过程、函数、子程序或模块)组成。
如果修改了其中任何一个程序单元都需要重新编译整个程序,将大大增加程序开发、调试和维护的成本;反之,如果可以独立编译,则只需要重新编译修改了的程序单元,然后重新连接整个程序即可。
由此可见,一个程序设计语言如果没有独立编译的机制,就不是适合软件工程需要的好语言。
独立编译的机制对于开发大型系统是极端重要的,这可能是在软件工程项目中广泛使用FORTRAN语言的一个主要原因。
FORTRAN语言虽然有不少缺点,但是它的子程序很容易独立编译。
块结构语言决定名字可见性的规则(多层局部性)使得这种语言不适于独立编译。
虽然在块结构语言的某些实现中增加了独立编译的机制,但是往往同时对使用施加了许多限制。
在Ada语言的设计中充分认识到了独立编译的必要性,因此语言中包含了独立编译的机制,它的package很适于独立编译。
三、选择一种语言
开发信息系统时必须做出的一个重要抉择是,使用什么样的程序设计语言实现这个系统。
适宜的程序设计语言能使根据设计去完成编码时困难最少,可以减少需要的程序测试量,并且可以得出更容易阅读和更容易维护的程序。
由于信息系统的绝大部分成本用在生命周期的测试和维护阶段,所以容易测试和容易维护是极端重要的。
使用汇编语言编码需要把软件设计翻译成机器操作的序列,由于这两种表示方法很不相同,因此汇编程序设计既困难又容易出差错。
一般说来,高级语言的源程序语句和汇编代码指令之间有一句对多句的对应关系。
统计资料表明,程序员在相同时间内可以写出的高级语言语句数和汇编语言指令数大体相同,因此用高级语言写程序比用汇编语言写程序生产率可以提高好几倍。
高级语言一般都容许用户给程序变量和子程序赋予含义鲜明的名字,通过名字很容易把程序对象和它们所代表的实体联系起来;此外,高级语言使用的符号和概念更符合人的习惯。
因此,用高级语言写的程序容易阅读、容易测试、容易调试、容易维护。
总的说来,高级语言明显优于汇编语言,因此,除了在很特殊的应用领域(例如,对程序执行时间和使用的空间都有很严格限制的情况;需要产生任意的甚至非法的指令序列;体系结构特殊的微处理机,以致在这类机器上通常不能实现高级语言编译程序),或者大型系统中执行时间非常关键的(或直接依赖于硬件的)一小部分代码需要用汇编语言书写之外,其他程序应该一律用高级语言书写。
那么,在种类繁多的高级语言中选择哪一种呢?
从程序设计语言的特点可以想到,为了使程序容易测试和维护以减少生命周期的总成本,选用的高级语言应该有理想的模块化机制,以及可读性好的控制结构和数据结构;为了便于调试和提高软件可靠性,语言特点应该使编译程序能够尽可能多地发现程序中的错误;为了降低软件开发和维护的成本,选用的语言应该有良好的独立编译机制。
上述这些要求是选择语言的理想标准,但是在实际选用语言时不能仅仅考虑理论上的标准,还必须同时考虑实用方面的各种限制。
重要的实用标准有下述几条:
(1)系统用户的要求
如果所开发的系统由用户负责维护,用户通常要求用他们熟悉的语言书写程序。
(2)可以使用的编译程序
运行目标系统的环境中可以提供的编译程序往往限制了可以选用的语言的范围。
(3)可以得到的软件工具
如果某种语言有支持程序开发的软件工具可以利用,则目标系统的实现和验证都变得比较容易。
(4)系统规模
如果系统规模很庞大,现有的语言又不完全适用,那么设计并实现一种供这个系统专用的程序设计语言,可能是一个正确的选择。
(5)程序员的知识
虽然对于有经验的程序员来说,学习一种新语言并不困难,但是要完全掌握一种新语言却需要实践。
如果和其他标准不矛盾,那么应该选择一种已经为程序员所熟悉的语言。
(6)软件可移植性要求
如果目标系统将在几台不同的计算机上运行,或者预期的使用寿命很长,那么选择一种标准化程度高、程序可移植性好的语言就是很重要的。
(7)软件的应用领域
所谓的通用程序设计语言实际上并不是对所有应用领域都同样适用,例如,FORTRAN语言特别适合于工程和科学计算,COBOL语言适合于商业领域应用,C语言和Ada语言适用于系统和实时应用领域,LISP语言适用于组合问题领域,PROLOG语言适于表达知识和推理。
因此,选择语言时应该充分考虑目标系统的应用范围。
6.1.2 程序设计方法与工具
一、程序设计方法
主要有两种程序设计方法,分别称为自顶向下的程序开发方法和自底向上的程序开发方法。
使用自顶向下的方法开发程序,程序员首先实现软件结构的最高层次,用“存根”代表较低层次的模块,所谓存根就是简化模拟较低层次模块功能的虚拟子程序。
实现了软件结构的一个层次之后,再用类似方法实现下一个层次,如此继续下去直到最终用程序设计语言实现了最低层次为止。
自底向上的方法和上述开发过程相反,从最底层开始构造系统,直至最终实现了最高层次的设计为止。
一般说来,用自顶向下的开发方法得到的程序可读性较好,可靠性也较高;用自底向上的开发方法得到的程序往往局部是优化的,系统的整体结构却较差。
但是,采用自底向上的开发方法能够及早发现关键算法是否可行,发生较大返工的可能性较小。
按照软件工程的方法论,编码之前应该经过总体设计和详细设计两个阶段的充分设计,编码只不过是把设计结果翻译成程序代码。
因此,不论采用上述哪种开发方法,对程序的结构和可读性都不会有多大影响,在这种情况下,两种开发方法的差别主要表现为测试策略的不同。
二、程序设计工具
使用适用的软件工具辅助程序设计,可以减轻人的劳动,提高生产率和程序的可靠性。
下面简单讨论几种程序设计工具。
1.编译程序
编译程序是最基本的程序设计工具。
如果编译程序能帮助程序员很快地诊断出程序中的差错,显然可以减少程序开发的成本。
此外,编译程序还应该能够生成高效率的机器代码。
后一项工作称为优化,它需要对源程序进行大量的分析,也就是需要耗费许多机器时间。
因此,在程序的开发过程中,每次编译都进行优化是很不经济的,程序设计环境应该为每种语言都提供两个兼容的编译程序---开发编译程序和优化编译程序。
开发编译程序应该能很快地编译源程序,并且给程序员提供详尽的诊断信息。
另一方面,优化编译程序应该能够生成高效的机器代码,但是对它的编译速度和诊断功能却并没有很高要求。
应该使用开发系统来开发程序,以降低开发成本,然后再使用优化系统对最终的程序进行编译,以得到用于生产现场的高效代码。
可以和编译系统结合在一起的一个重要工具是交叉参照程序,它能给出程序对象的名字和类型,程序中说明每个名字的位置(行号),以及访问每个对象的语句的行号。
更复杂的交叉参照程序还能提供每个模块的参数表和参数类型,模块的局部变量表和模块引用的全程变量表。
当需要修改某个全程变量时,这类信息很有用处。
2、代码管理系统
一个大型信息系统开发项目通常有许多程序员参加编码,程序代码往往分散在许多不同的文件或库中,而且可能既有源程序代码又有目标代码。
可能在不同时期会生产出同一个系统的许多不同版本,这些不同的版本分别适合于不同环境的需要。
与大型信息系统相联系的主要问题是,记录程序模块开发和维护的历程,确定模块间的相互依赖关系,保证在同一个系统的不同版本中的公共代码是一致的。
6.2 系统调试与转换
6.2.1 系统调试
一、程序调试
程序的正确性验证一般有理论法和实验法两种。
理论法是属于程序正确性证明问题,它是利用数学方法证明程序的正确性。
程序证明是一个令人鼓舞的方法,但尚处于研究之中,近期内还不能达到实用阶段。
目前,程序正确性验证中普遍采用的仍是实验法。
程序只有经过实验法调试,才能认为程序基本正确,而要证明程序完全正确,则要经过一段时间试用才能确定。
1、代码测试
调试过程中通常要编写测试数据。
测试数据除采用正常数据外,还应包括一些异常数据和错误数据,用来测试程序逻辑上的正确性。
测试数据是经过精心挑选的,使程序和模块中的每一条语句都能得到执行,即能够测试程序中的任一逻辑路径。
常用的测试数据有以下几种:
(1)用正常数据调试。
(2)用异常数据调试。
例如用空数据文件参加测试,检查程序能否正常运行。
(3)用错误数据调试。
试验程序对错误的处理能力,包括显示出错信息以及容许修改错误的可能性。
具体检查内容有:
·输入键号错误(包括错的键号和不应有的键号)时,能否及时检出和发出出错信息,并允许修改;
·输入数据错误(包括错误数据、不合理数据)时,能否及时查出或发出出错信息,并容许修改:
·操作错误(包括磁盘错误,操作步骤或方法错误)时,能否及时检出并发出警告信息,并允许改正。
2、程序功能测试
经代码测试正确的程序只是基本上验证了程序逻辑上的正确性,但并不能验证程序是否满足程序说明中定义的功能,也不能验证测试数据本身是否完备。
程序功能测试则面向程序应用环境,把程序看作一个“黑盒子”,认为程序只要满足应用功能上的需求,就是可行的。
二、分调
系统的应用软件通常由多个功能模块组成,每个模块由一个或几个程序构成。
在单个程序调试完成以后,尚需进行分调,即将一个功能内所有程序按次序串联起来进行调试。
这种调试的目的是要保证模块内各程序间具有正确的控制关系,同时可以测试模块的运行效率。
三、总调
总调的内容包括两部分:
1、主控程序和调度程序调试
这部分程序的语句不多,但逻辑控制复杂。
调试时,将所有控制程序与各功能模块的接口“短路”,即用直接送出预先安排计算结果的联系程序替代原功能模块。
调试的目的不是处理结果的正确性,而是验证控制接口和参数传递的正确性,以及发现并解决资源调度中的问题。
2、程序的总调
功能模块和控制程序调试完成后,即可进行整个系统程序的总调,也就是将主控制和调度程序与各功能模块联结起来进行总体调试。
对系统各种可能的使用形态及其组合在软件中的流通情况进行测试。
这一阶段查出的往往是模块间相互关系方面的错误和缺陷。
四、性能测试
除了上述常规测试之外,还有一些必要的性能测试。
这些测试往往不是针对程序在正常情况下运行的正确与否,而是根据系统需求选择进行的,主要有:
峰值负载测试、容量测试、响应时间测试、恢复能力测试等。
进行系统程序调试时,没有必要按完全真实情况下的数据量进行。
通常采用“系统模型”法以便以最少的输入数据量完成较全面软件测试。
通过对数据的精心选择,大大减少了输入数据量,不仅可以使处理工作量大为减少,而且也更容易发现错误和确定错误的范围。
调试中要严格核对计算机处理和人工处理的两种结果。
通常是先校对最终结果,发现错误再回到相应中间结果部分校对,直到基本确定错误范围。
系统测试完成后,在交付用户使用之前,还需要进行实况测试。
实况测试以过去手工处理方式下得出正确结果的数据作为输入,将系统处理结果与手工处理结果进行比较。
这一阶段,除严格校对结果外,主要考察系统运转的合理性、效率性、可靠性。
系统调试完成后,应编写操作说明书,完成程序框图和打印源程序清单。
6.2.2 系统转换
在系统调试完毕的基础上,进行系统转换工作。
此处的系统转换包括原来全部用人工处理的系统转换到新的以计算机为基础的信息系统,也包括从旧的信息系统向新的信息系统的转换过程。
转换工作包括旧系统的数据文件向新系统数据文件转换,人员、设备、组织机构的改造和调整,有关资料的建档和移交等。
系统转换的终结形式是将全部控制权移交用