自从计算机诞生以来,程序设计语言和程序设计方法不断地改进和发展,经历了从面向具体计算机的低级语言,发展到与英语语句书写形式类似的高级语言;从面向过程的程序设计语言,发展到可视化的面向对象的程序设计语言;从个人手工设计,发展到利用软件工程的办法组织众多人一起开发的过程。程序设计工具从单纯利用计算机语言发展到集成开发环境,设计方法从面向过程发展到面向对象。如今利用软件工程的方法组织多方协作开发较大型软件已成为普遍采用的方法。
要想设计、开发出健壮、易用的软件,除了熟悉自己使用的程序设计语言外,还必须掌握程序设计的规律。本章主要介绍程序设计的有关概念、数据结构和算法以及软件工程基础知识。
本节将介绍与程序设计有关的一些基础知识,包括程序设计的基本思想、方法和编程风格等,结构化程序设计的原则、基本结构和基本工具,以及面向对象的程序设计的基本思想。
程序设计方法学是关于用一定的观点研究问题并进行求解,以及如何进行系统构造的软件方法学。程序设计需要相应的理论、技术、方法和工具来支持。常用的程序设计方法有:结构化程序设计方法和面向对象的程序设计方法。
程序设计风格是指编写程序时所表现的特点、习惯和逻辑思路。著名的“清晰第一,效率第二”的观点已成为当今主导的程序设计风格。良好的程序设计风格可以使程序结构清晰合理,便于阅读和维护,减少编码的错误,提高程序的开发效率。因此,良好的程序设计风格对于好的程序设计具有关键性的作用。
要形成良好的程序设计风格,主要应注重和考虑以下几个因素:源程序文档化、数据说明、数据结构、输入输出方法等。
(1)源程序文档化。
文档是软件的重要组成部分,是软件生存周期各个不同阶段的产品描述。程序的文档能使程序容易被阅读和理解。一般应注意下面几点:
· 符号名的命名。命名约定能使程序更容易理解,同时也使得程序员在编写代码时更容易决定事物的命名。符号名(如模块名、变量名、常量名等)的命名应具有一定的实际含义,以便于对程序功能的理解。
· 程序注释。程序注释的目的是增加程序的可读性。通常用自然语言书写,给出程序的整体说明以及相关语句或程序段的功能说明。注释一般分为序言性注释和功能性注释。序言性注释通常位于每个程序的开始部分,给出程序的整体说明,描述的内容包括:程序标题、程序功能说明、主要算法、接口说明、程序位置、开发简历、程序设计者、复审者、复审日期、修改日期等。功能性注释的位置一般嵌在源程序中,主要描述其后语句或程序的功能。
· 程序的组织结构。指合理使用空格、空行和缩进等元素,通过格式化的书写方式使程序的代码清晰可读,使程序的逻辑结构清晰,层次分明。
(2)数据说明的方法。
数据说明是对程序中数据结构的组织和描述,数据说明应当规范有序,便于阅读和查找,一般应注意下面几点:
· 数据说明的层次应规范,以便于程序阅读、理解和维护,也有利于测试、排错和维护。
· 说明语句中变量安排有变化。当多个变量名用一个语句说明时,变量按照字母顺序排列。
· 注释是用来说明复杂数据结构的。
(3)语句结构。
语句结构应尽量简单清晰,短的代码通常更清楚。不能为了片面追求效率而使语句复杂化,应当分解复杂的语句,使之清晰易懂。通常遵循以下原则:
· 从数据出发来构造程序。
· 一行内只写一条语句。
· 避免使用临时变量而降低程序的可读性。
· 利用信息屏蔽,确保每一模块功能的独立性。
· 程序设计要模块化,模块功能应尽可能单一化。
· 使用条件应避免太复杂。
· 尽量减少使用“否定”条件的条件语句。
· 避免使用不必要的转移语句。
· 除对效率有特殊的要求外,程序的编写应做到“清晰第一,效率第二”的原则。
· 程序编写应优先考虑清晰性。
· 尽量使用库函数。
· 数据结构要有利于程序的简化。
· 首先要保证程序的正确性,然后才要求提高程序的速度。
· 对不好的程序不要修补,应当重新编写。
(4)输入和输出。
用户使用程序时最关心的是输入/输出格式(用户界面),输出应符合用户的要求,输入应合乎用户的习惯,同时方便用户的输入。所以无论是批处理的输入输出方式、还是交互式的输入输出方式,在程序设计时均需要考虑以下因素:
· 输入格式简单,操作方便。
· 对交互式输入输出,在屏幕上应有相应的提示信息,便于用户的操作。
· 对所有输入输出数据要检查数据的合法性。
· 应允许使用默认值。
· 要检查输入数据各种重要组合的合理性。
· 输入应允许使用自由格式。
· 对一批数据的输入,最好使用输入结束标志。
· 对输入格式有严格要求的程序设计语言,应保持输入格式与输入语句的一致性;并给所有的输出加注释,同时设计输出报表格式。
结构化程序设计是面向过程的程序设计常用的方法。它的主要特点是:先把计算机要处理的事务分解成若干个独立的过程(Procedures),自顶向下不断地把复杂的处理分解为子处理,一层一层地分解下去,直到仅剩下若干个容易处理的子处理为止。再用面向过程的开发语言(汇编、C语言等)编制源程序,然后经过编译形成计算机可执行程序,通过反复修改最后完成设计工作。
由于结构化程序具有结构清晰、便于阅读、便于修改和便于维护的优点,因此结构化程序设计方法已经被绝大多数程序员所采用。适合开发小型应用软件。
随着程序规模与复杂性的不断增长,人们也在不断探索新的程序设计方法,其中最受关注的是结构化程序设计方法,它起源自对goto语句的认识和争论。
结构化程序设计方法引入了工程思想和结构化思想,使大型软件的开发和编程都得到了极大的改善。它的主要原则可以概括为自顶向下、逐步求精、模块化、限制使用goto语句。
(1)自顶向下。
程序设计时,应先考虑总体,后考虑细节;先考虑全局目标,后考虑局部目标。不要一开始就追求众多的细节,先从最上层总目标开始设计,逐步使问题具体化。
(2)逐步求精。
对复杂问题,应设计一些子目标作过渡,逐步细化。
(3)模块化。
一个复杂问题,肯定是由若干稍简单的问题构成的。模块化是把程序要解决的总目标分解为分目标,再进一步分解为具体的小目标,把每个小目标称为一个模块。
(4)限制使用goto语句。
goto语句的使用会导致程序流程的混乱,因此应限制使用。
结构化程序具有如下的特征:
结构化程序把过程分为顺序、条件转移(循环)、分支、子过程等标准程序功能模块,每块都只能有唯一的入口和出口,模块之间通过入口、出口连接。
· 一个程序单元由顺序、分支和循环三种基本结构组成。
· 一个大的程序由若干个不同功能的小模块组成。
· 每一个小模块只用一个入口和一个出口。
结构化程序设计方法是程序设计的先进方法和工具。采用结构化程序设计方法编写程序,可使程序结构良好、易读、易理解、易维护。1966年,Boehm和Jacopini证明了程序设计语言仅仅使用顺序、选择和循环三种基本控制结构即可实现任何单入口/单出口的程序。
(1)顺序结构。
顺序结构是一种简单的程序设计,即按照程序语句行的自然顺序,一条语句一条语句地执行程序,它是最基本、最常用的结构。
(2)选择结构。
选择结构又称分支结构,该结构可根据设定的条件,判断下一步的执行的语句序列。
(3)循环结构。
循环结构即根据给定的条件,判断是否需要重复执行某一相同的程序段。利用循环结构可简化大量的程序行。在程序设计语言中,循环结构有两类语句,对先判断后执行循环体的称为当型循环结构;对先执行循环体后判断的称为直到型循环结构。
采用结构化程序的设计方法设计出的程序,具有下列优点:
(1)程序易于理解、使用和维护。
程序员采用结构化编程方法,便于控制、降低程序的复杂性,因此容易编写程序,便于验证程序的正确性,结构化程序清晰易读,可理解性好,程序员能够进行逐步求精、程序证明和测试,以确保程序的正确性,程序容易阅读并被理解,便于用户使用和维护。
(2)提高了编程工作的效率,降低了软件的开发成本。
面向对象程序设计方法是一种把面向对象的思想运用于软件开发过程中,指导开发活动的系统方法,简称OO方法,是建立在“对象”概念(对象、类和继承)基础上的方法学。其基本思想是:对问题空间进行自然分割,以更接近人类思维的方式,建立问题域模型,以便对客观实体进行结构模拟和行为模拟,从而使所设计出的软件尽可能直接地描述现实世界,构造出模块化的、可重用的、维护性好的软件,并能够控制软件的复杂性和降低开发维护费用。
采用面向对象的方法不仅为人们提供了较好的开发风格,而且在提高软件的生产率,可靠性、可重用性、可维护性等方面有明显的效果,已成为当今计算机界最为关注的一种开发方法。面向对象方法的本质,就是主张从客观世界固有的事物出发来构造系统,提倡用人们在现实生活中常用的思维方式来认识、理解和描述客观事物,强调最终建立的系统可以映射问题域,也就是说,系统中的对象以及对象与对象之间的关系能够如实反映问题域中固有事物及其联系。
面向对象方法成为目前流行的软件开发方法,基于面向对象方法有下列优点:
(1)与人们习惯的思维方法一致。
面向过程的程序设计是以算法为核心,把数据(代表问题空间中的客体)和过程(用于处理数据)作为相互独立的部分,在计算机内部数据和程序是分开存放的。用这种方法设计出来的软件系统其解空间和问题空间不一致,使人感到难于理解。其原因是面向过程的程序设计方法忽略了数据和操作之间的内在联系。
实际上,用计算机解决的问题都是现实世界中的问题,这些问题无非由一些相互存在一定联系的事物所组成,每个具体的事物都有行为和属性两个方面的特征。因此把描述事物静态属性的数据结构和表示事物动态行为的操作放在一起构成一个整体,才能完整、自然地表示客观世界中的实体。面向对象方法以对象为核心,对象是由数据和允许的操作组成的封装体,与客观实体有直接的对应关系。对象之间通过传递消息互相联系,以模拟现实世界中不同事物彼此之间的联系。
面向对象的设计方法的基本原理是使用现实世界的概念抽象地思考问题,从而自然地解决问题,强调模拟现实世界中的概念而不强调算法,鼓励开发者在软件开发的绝大部分过程中都用应用领域的概念去思考。
(2)稳定性好。
面向对象的软件系统是根据问题领域的模型建立起来的,而不是基于系统应完成的功能(面向过程的程序的软件系统)的分解,因此当系统的功能需求变化时并不会引起软件结构的整体变化,只需要做一些局部修改即可。由于现实世界中的实体是相对稳定的,因此以对象为中心构造的软件系统也是比较稳定的。但对于面向过程构造的软件系统,它以算法为核心,开发过程基于功能分析和功能分解,因此当功能需求发生变化时将引起软件系统的整体修改,因此是不稳定的。
(3)可重用性好。
软件的可重用性指软件本身的可重用性,即软件代码实现的可重用性。实际上,软件开发生命周期都有可重用的价值,包括项目的组织、软件需求、设计、文档、实现、测试方法和测试用例都是可以被重复利用或借鉴的有效资源。重用是提高软件生产效率最主要的方法。
传统的软件重用技术是使用标准函数库,也就是用这些函数库作为“预制件”来构造软件系统。这些函数只提供最基本的、常用的功能,因此开发者还必须自己编写函数。因此这些标准函数不能适应不同应用场合的不同需要,并不能作为理想的可重用软件部分。
面向对象的软件开发技术在利用可重用的软件成分构造新的软件系统时,灵活性大。有两种方法可以重复使用一个对象类:创建该类的实例;从它派生出一个满足当前需要的新类。
(4)易于开发大型软件产品。
对于大型软件的开发,组织开发不恰当往往是出现问题的主要原因。用面向对象的方法开发软件时,可以把一个大型产品看成是一系列相互独立的小产品来处理,这就不仅降低了开发的技术难度,而且也使得对开发工作的管理变得容易。
(5)可维护性好。
面向过程开发出的软件难于维护,是软件危机的突出表现。而面向对象的方法开发软件,由于软件的稳定性好、容易修改、易于理解、易于测试和调试,因此面向对象的方法开发软件具有可维护性好的特点。
(1)对象(object)。
在面向对象的程序设计中,“对象”是程序的基本单位,是一组数据(属性)和施加于这些数据上的一组操作代码(操作)构成的逻辑实体。
面向对象的程序设计中涉及的对象是系统中用来描述客观事物的一个实体,是构成系统的一个基本单位,它由一组表示其静态特征的属性和它可执行的一组操作组成。属性即对象所包含的信息,它在设计对象时确定,一般只能通过执行对象的操作来改变。
操作描述了对象执行的功能,若通过消息传递,还可为其他对象使用。操作的过程对外是封闭的,即用户只能看到这一类操作实施的后果,也就是说事先已设计好了各种过程,用户只要调用就可以了,不必关心这一过程是如何实现的。事实上,这些过程封装在对象中,用户看不到,这就是对象的封装性。
(2)消息(message)。
消息是对象间通信的手段,一个对象通过向另一个对象发送消息来请求服务,接受消息的对象经过解释,给与响应。消息通常包括接收对象、调用的操作名和适当的参数。
(3)属性、事件和方法。
属性是对象具有的特征或某一方面的行为。
事件是指对象能够识别的动作,可以编写相应的代码对此动作进行响应。事件可由系统发生,也可由用户执行某种操作来发生。
方法是对象能够执行的操作,是对象具有的功能体现,是实现每条消息具体功能的手段。
(4)类(class)和实例(instance)。
类是一组具有相同属性和相同操作对象的集合,因此类是对象的抽象,它描述了该对象类型的所有对象的性质,而一个对象则是其对应类的一个实例。
(5)继承(inheritance)。
继承是表达类之间相似性的一种机制,即在已有类的基础上增加构造新的类,前者称为父类,后者称为子类。子类除自动具有父类的全部属性和操作外,还可进一步定义新的属性和操作。若子类只从一个父类继承,称为单一继承;若子类从多个父类继承,则称为多重继承。
(6)封装。
封装性说明包含和隐藏对象信息(如内部数据结构和代码)的能力。封装使操作对象的内部复杂性与应用程序的其他部分隔离开来。封装和隐藏是面向对象技术核心,它使软件具有很好的模块性,各模块具有明显的范围和边界,实现了模块内的高内聚和模块间的低耦合。
(7)多态性(polymorphism)。
多态性是指同一操作在不同对象上可以有不同的解释,并产生不同的执行结果。
面向对象的开发模型将开发阶段分为:面向对象分析(Object Oriented Analysis,OOA)、面向对象设计(Object Oriented Design,OOD)和面向对象编程(Object Oriented Progra-
mming,OOP)三个阶段。分析阶段产生整个问题空间的抽象描述,在此基础上,进一步归纳出适用于面向对象编程语言中所用的类和类结构,最后形成代码。
要在应用程序的统筹规划和设计之后作以下步骤:
(1)创建对象或选用合适的对象。
(2)设置对象的属性。
(3)选择并设计适当的对象事件及操作。
(4)在过程代码中调用对象以实现对象之间的通信。
(5)将对象的方法程序和属性代码包装在一起。