
1.2 程序设计方法
1.2.1 结构化程序设计
1.结构化程序设计方法的出现
E.W.Dijkstra在1965年提出的结构化程序设计方法是软件发展中的一个重要的里程碑,它的主要观点是采用自顶向下和逐步求精的程序设计方法,并且任何程序都可由顺序、选择、循环3种基本控制结构构造。该方法以模块化设计为中心,将待开发的软件系统划分为若干个相互独立的模块。这样使完成每一个模块的工作变得单纯而明确,为设计一些较大的软件打下了良好的基础。
该方法的要点如下。
(1)自顶而下,逐步求精。
该设计思想的出发点是从问题的总体目标开始抽象低层的细节,首先专心构造高层的结构,然后一层一层地分解和细化。这使设计者能把握主题,高屋建瓴。从而避免一开始就陷入复杂的细节中,使复杂的设计过程变得简单明了,过程的结果也容易正确可靠。
(2)使用3种基本控制结构构造程序。
任何程序都可由顺序、选择、循环3种基本控制结构构造。
· 用顺序方式对过程分解,确定各个部分的执行顺序。
· 用选择方式对过程分解,确定某个部分的执行条件。
· 用循环方式对过程分解,确定某个部分重复的开始和结束的条件。
对处理过程仍然模糊的部分反复使用以上分解方法,最终可将所有细节确定下来。
(3)主开发人员组的组织形式。
开发人员组织方式应采用一个主开发人员(负责全部技术活动)、一个后备开发人员(协调、支持主开发人员)和一个程序管理员(负责事务性工作,如收集和记录数据、文档资料管理等)为核心,再加上一些专家(如通信专家、数据库专家)和其他技术人员组成小组。
2.基本结构
按照结构化程序设计的观点,任何算法和功能的实现都可以通过由程序模块组成的3种基本结构的组合,即顺序结构、选择结构和循环结构组成。
(1)顺序结构。
顺序结构是自然的顺序,由前到后执行,如图1-1所示。由图中可以看出这两个程序模块是按顺序执行的,即首先执行A语句,然后执行B语句。从逻辑上看,顺序结构中的两个程序模块可以合并成一个新的程序模块,通过这种方法可以将许多顺序执行的语句合并成一个比较大的程序模块。
(2)选择结构。
选择结构如图1-2所示。

图1-1 顺序结构

图1-2 选择结构
从图中可以看出根据逻辑条件成立与否分别选择执行A或者B。虽然选择结构比顺序结构稍微复杂一些,但是仍然可以将其整体作为一个新的程序模块,即一个入口(从顶部进入模块开始判断)和一个出口(无论是执行A还是B都应从选择结构框的底部退出)。
(3)循环结构。
循环结构有两种格式,如图1-3所示。

图1-3 两种循环结构
在图1-3(a)(当型循环)中进入循环结构后首先判断条件P是否成立,如果成立,则执行A,反之则退出循环结构;在图1-3(b)(直到型循环)中执行A后判断条件P,如果条件仍然成立,则再次执行内嵌的A。循环反复,直至条件不成立时退出循环结构。与顺序结构和选择结构相同,循环结构也可以抽象为一个新的模块。
3.设计原则
(1)自顶向下。
程序设计时应先考虑总体,后考虑细节;先考虑全局目标,后考虑局部目标。不要一开始就过多追求众多的细节,应从最上层总目标开始设计逐步使问题具体化。
(2)逐步细化。
对复杂问题,应设计一些子目标作为过渡,逐步细化。
(3)模块化。
一个复杂问题肯定是由若干稍简单的问题构成的。模块化是把程序要解决的总目标分解为子目标,再进一步分解为具体的小目标,把每一个小目标称为“一个模块”。
4.限制使用goto语句
结构化程序设计方法的起源来自对goto语句的认识和争论,肯定的结论是在查模块和进程的非正常出口处往往需要用goto语句,使用goto语句会使程序执行效率较高。在合成程序目标时goto语句往往是有用的,如返回语句用goto;否定的结论是goto语句是有害的,是造成程序混乱的祸根。程序的质量与goto语句的数量呈反比,应该在所有高级程序设计语言中取消goto语句。取消后程序易于理解、排错、维护,以及进行正确性证明。作为争论的结论,1974年Knuth发表了令人信服的总结并证实了如下观点。
(1)goto语句确实有害,应当尽量避免。
(2)完全避免使用goto语句也并非是个明智的方法,有些地方使用goto语句会使程序流程更清楚、效率更高。
(3)争论的焦点不应该放在是否取消goto语句上,而应该放在用什么样的程序结构上,其中最关键的是应在以提高程序清晰性为目标的结构化方法中限制使用goto语句。
1.2.2 面向对象程序设计
1967年挪威计算中心的Kisten Nygaard和Ole-Johan Dahl开发了Simula 67语言,它提供了比子程序更高一级的抽象和封装,并且引入了数据抽象和类(Class)的概念,被认为是第1个面向对象(Object)语言。
20世纪70年代初,Palo Alto研究中心的Alan Kay所在的研究小组开发出Smalltalk语言,之后又开发出被认为是最纯正的面向对象语言Smalltalk-80。它对后来出现的面向对象语言,如Object-C、C++、Self、Eiffl都产生了深远的影响。
随着面向对象语言的出现,面向对象程序设计也应运而生并迅速发展。之后面向对象的思想不断向其他阶段渗透,1980年Grady Booch提出了面向对象设计的概念,由此开始了面向对象分析。
1985年,第1个商用面向对象数据库问世,1990年以来面向对象分析、测试、度量和管理等研究都得到长足发展。
实际上“对象”和“对象的属性”这样的概念可以追溯到20世纪50年代初,它们首先出现在有关人工智能的早期著作中。但是出现了面向对象语言之后,面向对象思想才得到了迅速的发展。过去的几十年中程序设计语言对抽象机制的支持程度不断提高,从机器语言到汇编语言再到高级语言,直到面向对象语言。汇编语言出现后开发人员避免了直接使用0和1,而是利用符号来表示机器指令,从而更方便地编写程序。当程序规模继续增长时出现了Fortran、C、Pascal等高级语言,这些高级语言使得编写复杂的程序变得容易,开发人员可以更好地应对日益增加的复杂性。但是如果软件系统达到一定规模,即使应用结构化程序设计方法,局势仍将变得不可控制。作为一种降低复杂性的工具,面向对象语言和面向对象程序设计也随之产生。
1.面向对象的基本概念
(1)对象。
对象是人们要进行研究的任何事物,从最简单的整数到复杂的飞机等均可看作对象。它不仅能表示具体的事物,还能表示抽象的规则、计划或事件。
(2)类。
类是一个共享相同结构和行为的对象集合,它定义了一种事物的抽象特点。通常来说,类定义了事物的属性及其可以做到的行为。举例来说,“狗”这个类会包含狗的一切基础特征。例如,它的孕育、毛皮颜色和吠叫的能力。类可以为程序提供模板和结构,一个类的方法和属性被称为“成员”。
(3)封装(Encapsulation)。
封装的第1层意思是将数据和操作捆绑在一起创造出一个新的类型的过程,第2层意思是将接口与实现分离的过程。
(4)继承。
继承是类之间的关系,在这种关系中一个类共享了一个或多个其他类定义的结构和行为,子类可以对基类的行为进行扩展、覆盖和重定义。
(5)组合。
组合描述了“有”关系,既是类之间的关系,也是对象之间的关系,在这种关系中一个对象或者类包含了其他对象和类。
(6)多态。
多态是类型理论中的一个概念,一个名称可以表示很多不同类的对象。这些类和一个共同超类有关,因此这个名称表示的任何对象可以以不同的方式响应一些共同的操作集合。
(7)动态绑定。
动态绑定也称“动态类型”,指的是一个对象或者表达式的类型直到运行时才确定。通常由编译器插入特殊代码来实现,与之对立的是静态类型。
(8)静态绑定。
静态绑定也称“静态类型”,指的是一个对象或者表达式的类型在编译时确定。
(9)消息传递。
消息传递指的是一个对象调用了另一个对象的方法。
(10)方法。
方法也称为“成员函数”,指对象上的操作,作为类声明的一部分来定义。方法定义了可以对一个对象执行的操作。
2.面向对象的特点
(1)抽象。
类的定义中明确指出类是一组具有内部状态和运动规律对象的抽象,抽象是一种从一般的观点看待事物的方法。它要求集中于事物的本质特征(内部状态和运动规律),而非具体细节或具体实现。面向对象鼓励用抽象的观点来看待现实世界,也就是说现实世界是一组抽象的对象,即类组成的。
(2)继承。
继承是子类自动共享父类之间数据和方法的机制,由类的派生功能体现。一个类直接继承其他类的全部描述,同时可修改和扩充。继承具有传递性,分为单继承(一个子类只有一父类)和多重继承(一个子类有多个父类)。类的对象是各自封闭的,如果没有继承机制,则类对象中数据、方法就会出现大量重复。继承不仅支持系统的可重用性,而且还促进系统的可扩充性。
(3)封装。
封装是一种信息隐蔽技术,它体现于类的说明,是对象的重要特性。封装使数据和加工该数据的方法(函数)封装为一个整体,以实现独立性很强的模块。使得用户只能见到对象的外特性(能接受哪些消息,具有哪些处理能力),而对象的内特性(保存内部状态的私有数据和实现加工能力的算法)对用户是隐蔽的。封装的目的在于把对象的设计者和对象的使用者分开,使用者不必知晓行为实现的细节,只须用设计者提供的消息来访问该对象。
(4)多态(覆盖)。
多态指对象根据所接收的消息而做出动作,同一消息被不同的对象接收时可产生完全不同的动作,这种现象称为“多态性”。利用多态性用户可发送一个通用的信息,而将所有的实现细节都留给接收消息的对象自行决定,这样同一消息即可调用不同的方法。例如,Print消息被发送给一张图或表时调用的打印方法与将同样的Print消息发送给一个正文文件而调用的打印方法会完全不同。多态性的实现受到继承性的支持,利用类继承的层次关系把具有通用功能的协议存放在类层次中尽可能高的地方。而将实现这一功能的不同方法置于较低层次,这样在这些低层次上生成的对象就能给通用消息以不同的响应。在面向对象程序设计语言中可通过在派生类中重定义基类函数(定义为重载函数或虚函数)来实现多态性。
面向对象设计是一种把面向对象的思想应用于软件开发过程中指导开发活动的系统方法,是建立在对象概念基础上的方法学。对象是由数据和允许的操作组成的封装体,与客观实体有直接对应关系,一个对象类定义了具有相似性质的一组对象。而继承是对具有层次关系的类的属性和操作进行共享的一种方式。所谓面向对象就是基于对象概念,以对象为中心,以类和继承为构造机制来认识、理解、刻画客观世界,以及设计、构建相应的软件系统。