1.2 任务二:结构化程序设计
程序设计是软件开发的核心,是指处理事情的先后次序。在计算机语言中,程序定义为完成特定任务的计算机指令的集合。程序设计的方法有多种,本书选用最容易理解的结构化设计方法。
任务描述
工欲善其事,必先利其器!开发软件必须树立正确的开发观念和基本的程序开发技巧,为今后专业化开发打好基础。
任务分析与设计
了解结构化程序设计的理念在软件开发中的重要地位,掌握结构化程序设计的精髓和技巧,选择最恰当的入门语言。
任务实现
1.结构化设计方法(Structured Design,SD)
从系统设计的角度出发,软件设计方法可以分为三大类。第一类是根据系统的数据流进行设计,称为面向数据流的设计或者过程驱动的设计,以结构化设计方法为代表。第二类是根据系统的数据结构进行设计,称为面向数据结构的设计或者数据驱动的设计,以LCP(程序逻辑构造)方法、Jackson系统开发方法和数据结构化系统开发(DSSD)方法为代表。第三类是面向对象的设计。
结构化设计方法是在模块化、自顶向下细化、结构化程序设计等程序设计技术的基础上发展起来的。该方法实施的要点是:建立数据流的类型,指明流的边界,将数据流图映射到程序结构,用“因子化”方法定义控制的层次结构,用设计测量和一些启发式规则对结构进行细化。
2.软件设计的原则
1)抽象化
对软件进行模块设计时,可以有不同的抽象层次。在最高的抽象层次上,可以使用问题所处环境的语言描述问题的解法;而在较低的抽象层次上,则采用过程化的方法。
(1)过程的抽象:在软件工程过程中,从系统定义到实现,每进展一步都可以看做是对软件解决方案的抽象化过程的一次细化。在软件计划阶段,软件被当做整个计算机系统中的一个元素来看待。在软件需求分析阶段,用“问题所处环境的为大家所熟悉的术语”来描述软件的解决方法。在从概要设计到详细设计的过程中,抽象化的层次逐次降低。当产生源程序时,到达最低的抽象层次。
(2)数据抽象:数据抽象与过程抽象一样,允许设计人员在不同层次上描述数据对象的细节。例如,可以定义一个数据对象student,并将它规定为一个抽象的数据类型,用它的构成元素来定义它的内部细节。
student的抽象:student { name;age;sex;}
此时,数据抽象student本身由另外一些数据“name(名字)、age(年龄)、sex(性别)”抽象构成。在定义student为一个抽象的数据类型之后,可以引用它来定义其他用数据对象,如“Rose,John”,而不必涉及student的内部细节。
使用student定义其他数据对象:student Rose,John。
(3)控制抽象:与过程抽象和数据抽象一样,控制抽象可以包含一个程序控制机制而无须规定其内部细节。控制抽象的例子就是在操作系统中用以协调某些活动的同步信号。
2)自顶向下,逐步细化
将软件的体系结构按自顶向下方式,对各个层次的过程细节和数据细节逐步细化,直到用程序设计语言的语句能够实现为止,从而最后确立整个的体系结构。最初的说明只是概念性地描述了系统的功能或信息,但并未提供有关功能的内部实现机制或有关信息的内部结构的任何信息。设计人员对初始说明仔细推敲,进行功能细化或信息细化,给出实现的细节,划分出若干成分。然后再对这些成分施行同样的细化工作。随着细化工作的逐步展开,设计人员就能得到越来越多的细节。
3)模块化
模块又称构件,是能够单独命名并独立地完成一定功能的程序语句的集合。例如,高级语言中的过程、函数、子程序等都可作为模块。
模块化是软件的一个重要属性。模块化的特性提供了人们处理复杂问题的一种方法,同时也使得软件能够被有效地管理。
软件系统的层次结构正是模块化的具体体现。也就是说,整个软件被划分成若干单独命名和可编址的部分,称为模块。这些模块可以被组装起来以满足整个问题的需求。
模块和其他用模块之间的接口应尽可能独立。
(1)系统设计通常分为结构设计和过程设计两个阶段。
① 结构设计:确定系统由哪些模块组成,以及这些模块之间的相互关系。结构设计是总体设计阶段的任务。
② 过程设计:确定每个模块的处理过程。过程设计是详细设计阶段的任务。
(2)设计的后处理:在确定系统的软件结构以后,还必须做好下述工作。
① 为每个模块开发一份功能说明。
② 为每个模块提供一份接口说明。
③ 定义局部的和全程的数据结构。
④ 给出所有的设计限制或约束。
⑤ 进行总体设计评审。
⑥ 如果需要和可能,则进行设计“优化”。
(3)模块的独立性与信息隐蔽原理。每个模块的实现细节(过程和数据)对于其他模块来说应该是隐蔽的,即包含在模块中的信息(过程或数据)对于其他不需要这些信息的模块来说,是不能访问的,或者是“不可见”的。
有效的模块化可以通过定义来实现,模块间的通信对于软件功能来说是必要的。通过信息隐蔽,可以实施对模块的过程细节和局部数据结构的限制。
模块的独立性是软件质量的关键:模块化程度较高的软件容易开发,模块化程度较高的软件也比较容易测试和维护。
4)控制层次
控制层次也称为程序结构,它表明了程序构件(模块)的组织情况。控制层次往往用程序的层次(树形或网状)结构来表示。
(1)程序结构的深度:程序结构的层次数称为结构的深度,反映了程序的规模和复杂程度。
(2)程序结构的宽度:同层次结构中的最大模块个数称为结构的宽度。
(3)模块的扇入和扇出:扇出表示一个模块直接调用(或控制)的其他用模块数目。扇入定义为调用(或控制)一个给定模块的模块个数。多扇出意味着需要控制和协调许多下属模块。多扇入的模块通常是公用模块。
5)结构的划分
程序结构可以按水平方向或垂直方向进行划分。
(1)水平划分按程序功能定义模块各分支。顶层模块是控制模块,用来协调程序各个功能之间的通信和运行;其下级模块的最简单的水平划分方法是建立三个分支:输入、处理(数据变换)和输出。优点是:主要的功能相互分离,易于修改、扩充。缺点是:模块接口传递更多的数据,程序流的整体控制复杂。
(2)垂直划分也称为因子划分。工作自顶向下逐层分布:顶层模块执行控制功能,而低层模块完成实际输入、计算和输出操作。优点是:低层模块的修改副作用小,程序控制结构修改的可能性小,便于维护。
6)数据结构
数据结构是数据元素间逻辑关系的表示。典型的数据种类是有限的,它们是构成一些更复杂结构的基本构件块。
7)软件过程
程序结构描述了整个程序的控制层次关系和各个部分的接口情况,而图1.6所示的模块A的软件过程则着重描述各个模块的处理细节。
软件过程必须提供详细的操作说明,如事件的顺序、正确的判定点、重复的操作等。程序结构与软件过程有关。模块的处理必须指明其上下级环境。软件过程也是层次化的。
图1.6 模块A的软件过程
3.编程语言
编程是一种行为,是根据用户制定的步骤用“程序语言”表示给计算机,“指挥”计算机完成任务。
算法是程序的逻辑描述,结构是逻辑的数据实体。设计出算法、描述出结构程序就有了雏形。
1)程序设计语言的分类
程序设计语言分为以下3类。
(1)机器语言。
(2)汇编语言。
(3)高级语言。
2)语言的选择
从描述客观系统的角度看,程序设计语言还可以分为:
(1)面向过程语言。
数据结构+算法 (首推C语言)
(2)面向对象语言。
对象+消息 (代表为C++、Java语言)
语言只是工具,编程语言的底层都是相似的,其差别主要是适用范围。
由于C语言是结构化的程序语言,适用于自顶向下的软件开发方法,在编码之前能帮助用户了解程序整体结构和操作,适合解决数据处理领域的实际问题。我们选择结构严谨、使用灵活的C语言作为我们的开发语言。该方法采自顶向下、逐步细化的设计方法,主要有确定问题、分析、设计算法、实现、测试、维护6个步骤。
引导文献
1.程序类型
源程序:用户编写的程序称为源程序。
目标程序:源程序通过翻译形成目标程序(.OBJ文件)。
运行程序:将目标程序与函数库连接后,形成运行程序(.EXE文件)。
程序设计:指用户通过编写源程序,翻译源程序作为目标程序,连接目标程序与函数库,形成运行程序并整理设计文档的全过程。
2.工程
把代码写到一个文件里,会使查找和修改不再麻烦,单元(Unit)文件是一个不错的选择。
3.模块与函数
要把大一些的任务分成小块,再分成更小的块,一个模块对应一个单元(函数)。这样的分工可以分散源代码,多人同步完成各个单元的代码,这就是结构化编程。在C语言中,各个模块一般体现为“函数”。函数是C语言中最基本的编程单位。
4.程序的设计风格
1)程序内部的文档
程序内部的文档包括标志符(变量和标号)、程序的注释、程序的视觉组织。
2)标志符
标志符包括模块名、变量名、常量名、标号名、子程序名以及数据区名、缓冲区名等。这些名字应含义鲜明、能正确地提示代表的实体且符合语言的命名规范。
3)程序的注释
程序的注释分为两种:序言性注释和功能性注释。
(1)序言性注释通常安排在每个程序模块的起始部分,它是对程序的整体说明。
(2)功能性注释嵌入在源程序体内,用以描述其后的语句或程序段的处理功能。
4)程序的视觉组织
程序中代码的布局对程序的可读性有很大的影响。适当地利用空格、空行和移行能使程序的逻辑结构更加清晰。空格的合理应用还可以突出运算的优先性,避免发生运算错误。
5)数据说明
为了使数据更容易理解和维护,数据说明应遵循一些简单、规范的原则。
6)输入/输出
以交互式输入/输出方式为主。对所有输入数据都进行校验,要指明可选择的值或界限,以保证每个数据的有效性/合法性;为输出加注释,并设计输出格式。
7)效率
效率是一个性能要求,应该在需求分析阶段提出要求;好的设计可提高效率;程序的效率和程序的简明程度是一致的。
5.概要设计与详细设计
软件设计是一个把软件需求变换成软件表示的过程。最初,这种表示只是描绘出软件的总框架,然后进一步细化,把它加工成在程序细节上非常接近于源程序的软件表示。从工程管理的角度来看,软件设计分两步完成:
(1)概要设计,将软件需求转化为数据结构和软件的系统结构。
(2)详细设计,即过程设计。通过对结构表示进行细化,得到软件的详细的数据结构和算法。
概要设计建立起整个系统的体系结构框架,并给出了系统中的全局数据结构和数据库接口,人机接口,与其他硬、软件连接的接口。此外,还从系统全局的角度,考虑处理方式、运行方式、容错方式以及系统维护等方面的问题,并给出了度量和评价软件质量的方法。它奠定了整个系统实现的基础。若没有概要设计,直接考虑程序设计,则不能从全局把握软件系统的结构和质量,实现活动处于一种无序状态,程序结构划分不合理,导致系统处于一种不稳定的状态,稍做改动就会失败。因此,不能没有概要设计。
6.完成良好的软件设计应遵循的原则
软件设计既是过程又是模型。设计过程是一系列迭代的步骤,使设计人员能够描述被开发软件的方方面面。设计模型体现了自顶向下、逐步细化的思想,首先构造事物的整体;然后逐步细化,引导设计人员构造各种细节。为了给软件设计人员提供一些指导,Davis于1995年提出如下一系列软件设计的原则,其中有些已被修改和补充。
(1)设计过程不应受“隧道视野”的限制。一位好的设计者应当考虑一些替代的手段。根据问题的要求,可以用基本的设计概念,如抽象、逐步求精、模块化、软件体系结构、控制层次、结构分解、数据结构、软件过程、信息隐蔽等,来决定完成工作的资源。
(2)设计应能追溯到分析模型。由于设计模型中的每一单个成分常常可追溯到多个需求上,因此有必要对设计模型如何满足需求进行追踪。
(3)设计不应当从头做起。系统是使用一系列设计模式构造起来的,很多模式很可能以前就遇到过。这些模式通常被称为可复用的设计构件。可以使用它们代替那些从头开始做的方法。时间短暂而资源有限!将应当设计时间投入到表示真正的新思想和集成那些已有的设计模式上。
(4)设计应当缩短软件和现实世界中问题的“智力差距”,也就是说,软件设计的结果应尽可能模拟问题领域的结构。
(5)设计应具有一致性和集成性。如果一个设计从整体上看像是一个人完成的,那么它就是一致的。在设计工作开始之前,设计小组应当定义风格和格式的规则。如果仔细定义了设计构件之间的接口,则该设计就是集成的。
(6)使用上述基本的设计概念,将设计构造得便于将来的修改。
(7)应将设计构造得即使遇到异常的数据、事件或操作条件,也能平滑地、轻松地降级。设计良好的计算机程序应当永不“彻底停工”,它应能适应异常的条件,并且当它必须中止处理时也能以从容的方式结束。
(8)设计不是编码,编码也不是设计。即使在建立程序构件的详细的过程设计时,设计模型的抽象级别也比源代码高。在编码级别上做出的唯一设计决策是描述如何将过程性设计转换为程序代码的小的实现细节。
(9)在开始着手设计时,就应当能够评估质量,而不是在事情完成之后。利用上述基本的设计概念和已有的设计方法,可以帮助设计者评估质量。
(10)应当坚持设计评审以减少概念上(语义上)的错误。有时,人们在设计评审时倾向于注重细节,只见树木不见森林。在关注设计模型的语法之前,设计者应能确保设计的主要概念上的成分(遗漏、含糊、不一致)都已检查过。