面向过程编程与面向对象编程体现了编程者的两种不同的思维方式,本节教程主要介绍面向过程编程与面向对象编程的区别和联系。
面向过程是一种以过程为中心的编程思想,它首先分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,在使用时依次调用,是一种基础的顺序的思维方式。面向过程开发方式是对计算机底层结构的一层抽象,它将程序分为数据和操纵数据的操作两部分,其核心问题是数据结构和算法的开发和优化。常见的支持面向过程的编程语言有 C语言、COBOL 语言等。
面向对象是按人们认识客观世界的系统思维方式,采用基于对象(实体)的概念建立模型,模拟客观世界分析、设计、实现软件的编程思想,通过面向对象的理念使计算机软件系统能与现实世界中的系统一一对应。
面向对象方法直接把所有事物都当作独立的对象,处理问题过程中所思考的不再主要是怎样用数据结构来描述问题,而是直接考虑重现问题中各个对象之间的关系。面向对象方法的基础实现中也包含面向过程的思想。常见的支持面向对象的编程语言有 C++ 语言、C# 语言、Java 语言等。
具体来说,面向对象与面向过程有以下四个方面的不同:
1) 出发点不同
面向对象使用符合常规思维的方式来处理客观世界的问题,强调把解决问题领域的“动作”直接映射到对象之间的接口上。而面向过程则强调的是过程的抽象化与模块化,是以过程为中心构造或处理客观世界问题。
2) 层次逻辑关系不同
面向对象使用计算机逻辑来模拟客观世界中的物理存在,以对象的集合类作为处理问题的单位,尽可能地使计算机世界向客观世界靠拢,以使处理问题的方式更清晰直接,面向对象使用类的层次结构来体现类之间的继承与发展。面向过程处理问题的基本单位是能清晰准确地表达过程的模块,用模块的层次结构概括模块或模块间的关系与功能,把客观世界的问题抽象成计算机可以处理的过程。
3) 数据处理方式与控制程序方式不同
面向对象将数据与对应的代码封装成一个整体,原则上其他对象不能直接修改其数据,即对象的修改只能由自身的成员函数完成,控制程序方式上是通过“事件驱动”来激活和运行程序的。而面向过程是直接通过程序来处理数据,处理完毕后即可显示处理的结果,在控制方式上是按照设计调用或返回程序,不能自由导航,各模块之间存在着控制与被控制,调动与被调用的关系。
4) 分析设计与编码转换方式不同
面向对象贯穿于软件生命周期的分析、设计及编码中,是一种平滑的过程,从分析到设计再到编码是采用一致性的模型表示,实现的是一种无缝连接。而面向过程强调分析、设计及编码之间按规则进行转换贯穿于软件生命周期的分析、设计及编码中,实现的是一种有缝的连接。
面向对象和面向过程的特性和优缺点对比如表 1 所示。
面向对象 | 面向过程 | |
---|---|---|
特性 | 抽象、继承、封装、多态 | 功能模块化,代码流程化 |
优点 | 易维护、易复用、易扩展、低耦合 | 性能高,适合资源紧张、实时性强的场合 |
缺点 | 性能比面向过程低 | 没有面向对象易维护、易复用、易扩展 |
面向对象的编程方法有四个基本特性:
1) 抽象:就是忽略一个主题中与当前目标无关的方面,以便更充分地注意与当前目标有关的方面。抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用部分细节。抽象包括两个方面,一是过程抽象,二是数据抽象。
过程抽象是指任何一个明确定义功能的操作都可被使用者看作单个的实体看待,尽管这个操作实际上可能由一系列更低级的操作来完成。数据抽象定义了数据类型和施加于该类型对象上的操作,并限定了对象的值,只能通过使用这些操作修改和观察。
2) 继承:这是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法。对象的一个新类可以从现有的类中派生,这个过程称为类继承。新类继承了原始类的特性,新类称为原始类的派生类(子类),而原始类称为新类的基类(父类)。
派生类可以从它的基类那里继承方法和实例变量,并且类可以修改或增加新的方法使之更适合特殊的需要。这也体现了大自然中一般与特殊的关系。继承性很好地解决了软件的可重用性问题。
3) 封装:就是把过程和数据包围起来,对数据的访问只能通过已定义的接口。面向对象的计算始于这个基本概念,即现实世界可以被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象。一旦定义了一个对象的特性,则有必要决定这些特性的可见性,即哪些特性对外部世界是可见的,哪些特性用于表示内部状态。
在这个阶段定义对象的接口。通常,应禁止直接访问一个对象的实际表示,而应通过操作接口访问对象,这称为信息隐藏。封装保证了模块具有较好的独立性,使得程序维护修改较为容易。对应用程序的修改仅限于类的内部,因而可以将应用程序修改带来的影响减少到最低限度。
4) 多态:是指允许不同类的对象对同一消息做出响应。比如同样的复制-粘贴操作,在字处理程序和绘图程序中有不同的效果。多态性包括参数化多态性和包含多态性。多态性语言具有灵活、抽象、行为共享、代码共享的优势,很好地解决了应用程序函数同名问题。
为了进一步理解面向对象和面向过程的不同,以设计一个五子棋程序为例,面向过程的设计思路是,首先分析问题的步骤:① 开始游戏;② 黑子先走;③ 绘制画面;④ 判断输赢;⑤ 轮到白子;⑥ 绘制画面;⑦ 判断输赢;⑧ 返回步骤 ②;⑨ 输出最后结果,然后将上面每个步骤用程序来实现即可。
面向对象的设计则将程序分为三类对象:① 黑白双方,这两方的行为是一模一样的;② 棋盘系统,负责绘制画面;③ 规则系统,负责判定诸如犯规、输赢等。第 ① 类对象(玩家对象)负责接受用户输入,并告知第 ② 类对象(棋盘对象)棋子布局的变化,棋盘对象接收到了棋子的变化就要负责在屏幕上面显示出这种变化,同时利用第 ③ 类对象(规则系统)来对棋局进行判定。
可见,面向对象是以功能来划分问题,而不是步骤。同样是绘制棋局,这样的行为在面向过程的设计中分散在了多个步骤中,很可能出现不同的绘制版本,而面向对象的设计中,绘图只可能在棋盘对象中出现,从而保证了绘图的统一。功能上的统一保证了面向对象设计的可扩展性。
如要加入悔棋功能,若是面向过程设计,则从输入到判断到显示的若干步骤都要改动,甚至步骤之间的先后顺序都可能需要调整。而若是面向对象设计,则只需改动第 ② 类对象(棋盘对象)即可,棋盘对象保存了黑白双方的棋谱和落子先后顺序,简单回溯操作即可实现悔棋功能,并不涉及显示和规则部分,改动是局部可控的。