
1.5 面向对象开发
面向对象(Object Oriented,OO)是软件开发方法,它是一个依赖于几个基本原则的思想库,目前已经席卷了整个软件界。面向对象是一种对现实世界理解和抽象的方法,是计算机编程技术发展到一定阶段后的产物,它强调在软件开发过程中面向客观世界或问题域中的事物,采用人类在认识客观世界的过程中普遍运用的思维方法,直观、自然地描述客观世界中的有关事物。目前,面向对象的概念和应用已超越了程序设计和软件开发,扩展到如数据库系统、交互式界面、分布式系统、网络管理结构、CAD技术、人工智能等领域。
1.5.1 面向对象的概念
面向对象不仅是一些具体的软件开发技术与策略,而且是一整套关于如何看待软件系统与现实世界的关系,用什么观点来研究问题并进行求解,以及如何进行系统构造的软件方法学。
面向对象的核心是对象,它是系统中用来描述客观事物的一个实体,它是构成系统的一个基本单位。一个对象由一组属性和对这组属性进行操作的一组服务组成。从更抽象的角度来说,对象是问题域或实现域中某些事物的一个抽象,它反映该事物在系统中需要保存的信息和发挥的作用;它是一组属性和有权对这些属性进行操作的一组服务的封装体。客观世界是由对象和对象之间的联系组成的。
面向对象的软件工程方法的基础是面向对象编程语言。一般认为,诞生于1967年的Simula-67是第一种面向对象的编程语言。尽管该语言对后来许多面向对象语言的设计产生了很大的影响,但它并没有后续版本。继而,20世纪80年代初,Smalltalk语言掀起了一场“面向对象”运动。随后便诞生了面向对象的C++、Eiffel和CLOS等语言。尽管当时面向对象的编程语言在实际使用中具有一定的局限性,但它仍被广泛关注,一批批面向对象编程书籍层出不穷。直到今天,面向对象编程语言数不胜数,在众多领域发挥着各自的作用,如C++、Java、C#、VB.NET和C++.NET等。随着面向对象技术的不断完善,面向对象技术逐渐在软件工程领域得到了应用。
面向对象的软件工程方法包括面向对象分析(OOA)、面向对象设计(OOD)、面向对象编程(OOP)等内容。
1.面向对象分析
OOA是应用面向对象方法进行系统分析的。OOA是面向对象方法从编程领域向分析领域发展的产物。从根本上讲,面向对象是一种方法论,不仅仅是一种编程技巧和编程风格,而且是一套可用于软件开发全过程的软件工程方法。OOA是其中第一个环节。OOA的基本任务是运用面向对象方法,从问题域中获取需要的类和对象,以及它们之间的各种关系。
2.面向对象设计
OOD指面向对象设计,在软件设计生命周期中发生于OOA之后。在面向对象的软件工程中,OOD是软件开发过程中的一个大阶段,其目标是建立可靠的、可实现的系统模型;其过程是完善OOA的成果,细化分析。其与OOA的关系为:OOA表达了“做什么”,而OOD则表达了“怎么做”,即分析只解决系统“做什么”,不涉及“怎么做”,而设计则解决“怎么做”的问题。
3.面向对象编程
OOP就是使用某种面向对象的语言,实现系统中的类和对象,并使得系统能够正常运行。在理想的OO开发过程中,OOP只是简单地使用编程语言实现了OOA和OOD分析和设计模型。
1.5.2 面向对象开发的概述
面向对象开发方法的原则是,鼓励软件开发者在软件生命周期内应用其概念来工作和思考。只有较好地识别、组织和理解了应用领域的内在概念,才能有效表达出数据结构和函数的细节。
面向对象开发只有到了最后几个阶段,才不是独立于编程语言的概念过程。所以,可以将面向对象开发看作是一种思维方式,而不是一种编程技术。它的最大好处在于帮助规划人员、开发者和客户清晰地表达出抽象的概念,并将这些概念互相传达。它可以充当规约、分析、文档、接口以及编程的一种媒介。
为了加深读者对面向对象开发的理解,下面将它与传统软件开发作比较。面向对象的开发方法把完整的信息系统看成对象的集合,用这些对象来完成所需要的任务。对象能根据情况执行一定的行为,并且每个对象都有自己的数据。而传统开发方法则把系统看成一些与数据交互的过程,这些数据与过程隔离保存在不同文件中,当程序运行时,就创建或修改数据文件。下图显示了面向对象开发与传统软件开发之间的区别。

过程通过接收输入的数据,对它进行处理,随后保存数据或输出数据。面向对象则是通过接收消息来更新它的内部数据。这些差别虽然看起来简单,但对整个系统的分析、设计和实现来说却非常重要。

任何一种开发方法,在开发系统之前,开发人员都会对此项目进行分析。系统需求分析就是对系统需求的研究、了解和说明。系统需求定义了系统要为从事业务活动的用户完成的任务。这些需求一般用图表来描述。对图表进行规范化后就构成了该系统的基本需求模型。系统分析过程中建立的模型被称为逻辑模型,因为它仅描述了系统的需求,所以不涉及如何实现此需求。系统设计就是建立一个新模型,该模型展示了组成软件系统所使用的技术。系统设计过程中建立的模型也称为物理模型。
在传统的结构化分析和设计中,开发人员也使用图形模型,如数据流图(DFD)用来表示输入、输出和处理,还要建立实体关系图(ERD),以表示有关存储数据的详细信息。它的设计模型主要由结构图等构成。
在OO开发中,因为需要描述不同的对象,所以OO开发中建立的模型不同于传统模型。例如,OO开发不仅需要用数据和方法来描述建模,还需要用模型来描述对象之间的交互。OO开发中使用UML来构造模型。
OO开发方法不仅在模型上与传统的开发方法不同,系统开发生命周期也不同。系统开发生命周期是开发一个项目的管理框架,它列出了开发系统时的每个阶段和在每个阶段所要完成的任务。几个主要的阶段有计划、分析、设计、实现和支持。系统开发生命周期最初用在传统的系统开发中,但它也能用在OO开发中。OO开发人员经常使用迭代开发方法来分析、设计和实现。
迭代开发方法就是先分析、设计,编写部分程序完成系统需求的一部分,然后再分析、设计、编程完成其他需求。下图演示了迭代开发方法。
迭代开发方法和早期瀑布开发方法形成了鲜明对比。在瀑布开发方法中,在开始设计前要完成所有的需求分析,然后在需求分析的基础上进行系统设计,编程工作要在系统分析和设计完成后才进行。虽然传统开发方法也使用了迭代开发方法,但是因为每个迭代过程都涉及改进和增加模块的功能,而且在OO开发过程中每次迭代只需增加一个类,所以OO开发比传统开发更适用于迭代开发。
OO方法在建造系统模型和描述系统如何工作方面和传统的编程方法不同,但在系统开发生命周期和项目管理中,OO开发仍然和传统系统开发有相似之处。
1.5.3 面向对象的主要特征
为了进一步理解面向对象的内涵,还需要进一步了解面向对象的主要特征。

1.抽象
抽象(Abstract)是忽略事物中与当前目标无关的非本质特征,更充分地注意与当前目标有关的本质特征,从而找出事物的共性,并把具有共性的事物划为一类,得到一个抽象的概念。
例如,在设计一个学生管理系统的过程中以学生李华为例,就只关心他的学号、班级、成绩等,而忽略他的身高、体重等信息。因此,抽象性是对事物的抽象概括和描述,实现了客观世界向计算机世界的转化。将客观事物抽象成对象及类是比较难的过程,也是面向对象方法的第一步。例如,将学生抽象成对象及类的过程如下图所示。
2.封装
封装是面向对象的一个重要原则。封装指将对象属性和操作结合在一起,构成一个独立的对象。它的内部信息是隐蔽的,不允许外界直接存取对象的属性,而只能通过指定的接口与对象联系。
封装使得对象属性和操作紧密结合在一起,这反映了事物的状态特性和动作与事物不可分割的特征。系统中把对象看成其属性和操作的结合体,就使对象能够集中而完整地描述一个事物,避免了将数据和功能分离开进行处理,从而使得系统的组成与现实世界中的事物具有良好的对应性。
封装的信息隐蔽作用反映出事物的独立性。这样使得对于对象外部而言,只需要注意对象对外呈现的行为,而不必关心其内部的工作细节。封装可以使软件系统的错误局部化,从而大大降低查错和排错的难度。另一方面,当修改对象内部时,由于它只通过操作接口对外部提供服务,因此大大减少了内部修改对外部的影响。
3.继承
继承是指子类可以拥有父类的全部属性和操作。继承是OO方法的一个重要概念,并且是OO技术可以提高软件开发效率的一个重要原因。
建造系统模型时,可以根据所涉及的事物的共性抽象出一些基本类,在此基础上再根据事物的个性抽象出新的类。新类既具有父类的全部属性和操作,又具有自己独特的属性和操作。父类与子类的关系为一般与特殊的关系。
继承机制具有特殊的意义。由于子类可以自动拥有父类的全部属性和操作,这样使得定义子类时,不必重复定义那些在父类中已经定义过的属性和操作,只需要声明该类是某个父类的子类,将精力集中在定义子类所特有的属性和操作上。这样就提高了软件的可重用性。
继承具有传递性。如果子类B继承了类A,而子类C又继承了类B,则子类C可以继承类A和类B的所有属性和操作。这样,子类C的对象除了具有该类的所有特性外,还具有全部父类的所有特性。
如果限定每个子类只能单独继承一个父类的属性和操作,则这种继承称为单继承。在有些情况下,一个子类可以同时继承多个父类的属性和操作,这种继承称为多重继承。
4.多态性
在面向对象的开发中,多态性是指在父类中定义的属性和操作被子类继承后,可以具有不同的数据类型或表现出不同的行为。例如,定义一个父类“几何图形”时,为其定义了一个绘图操作。当子类“椭圆”和“矩形”都继承了几何图形类的绘图操作时,该操作根据不同的类对象将执行不同的操作。在“椭圆”类对象调用绘图操作时,该操作将绘制一个椭圆,而当“矩形”类对象执行该操作时,将绘制一个矩形。这样,当系统的其他部分请求绘制一个几何图形时,同样的“绘图”操作消息因为接收消息的对象不同,将执行不同的操作。
在父类与子类的类层次结构中,利用多态性可以使不同层次的类共享一个方法名,而各自有不同的操作。当一个对象接收到一个请求消息时,采取的操作将根据该对象所属的类决定。
在继承父类的属性和操作的名称时,子类也可以根据自己的情况重新定义该方法。这种情况称为重载。重载是实现多态性的方法之一。
5.关联
在现实世界中,事物不是孤立的、互相无关的,而是彼此之间存在着各种各样的联系。例如,在一个学校中,有教师、学生、教室等事物,它们之间存在着某种特定的联系。在面向对象的方法中,用关联来表示类或对象集合之间的这种关系。在面向对象中,常把对象之间的连接称为链接,而把存在对象连接的类之间的联系称为关联。
如果在OOA和OOD阶段定义了一个关联,那么在实现阶段必须通过某种数据结构来实现它。关联还具有多重性,多重性表示关联的对象之间数量上的约束,有一对一、一对多、多对多等不同的情况。
6.聚合
现实世界中既有简单的事物,也有复杂的事物。当人们认识比较复杂的事物时,常用的思维方法为:把复杂的事物分解成若干个比较简单的事物。在面向对象的技术中,像这样将一个复杂的对象分解为几个简单对象的方法称为聚合。
聚合是面向对象方法的基本概念之一。它指定了系统的构造原则,即一个复杂的对象可以分解为多个简单对象。同时,它也表示为对象之间的关系:一个对象可以是另一个对象的组成部分,同时,该对象也可以由其他对象构成。
7.消息
消息是指对象之间在交互中所传递的通信信息。当系统中的其他对象需要请求该对象执行某个操作时,就向其发送消息,该对象接收消息并完成指定的操作,然后把操作结果返回到请求服务的对象。
一个消息一般应该含有如下信息:接收消息的对象、请求该对象提供的服务、输入信息和响应信息。
消息在面向对象的程序中具体表现为函数调用,或其他类似于函数调用的机制。对于一个顺序系统,由于其不存在并发执行多个任务的情况,其操作是按顺序执行的,因此其消息实现目前主要为函数调用。而在并发程序和分布式程序中,消息则为进程间的通信机制和远程调用过程等其他通信机制。
1.5.4 面向对象的层
面向对象的开发中,通常把面向对象系统中相互联系的所有对象分成3层:数据访问层、业务逻辑层和表示层,区分层次的目的是遵循“高内聚、低耦合”的思想。它们的作用如下。
1.数据访问层
主要是针对原始数据(数据库或者文本文件等存放数据的形式)进行操作的层,而不是指原始数据。也就是说,是对数据的操作,而不是数据库,具体为业务逻辑层或表示层提供数据服务。
2.业务逻辑层
主要是针对具体问题的操作,也可以理解成对数据层的操作,对数据业务进行逻辑处理。如果说数据层是积木,那逻辑层就是对这些积木的搭建。

3.表示层
简单来说,表示就是展现给用户的界面,即用户在使用一个系统时的所见所得,像菜单、按钮和输入框等都属于这一层。下图是在图书管理系统中添加学生信息和借书信息操作时的三层过程。
从上图中可以看出,管理员和图形用户界面(表示层)交流,图形用户界面一般由包含表示对象的窗口组成,窗口中包含按钮、菜单、工具栏的窗体。用户不能直接和业务逻辑层交互,而是通过鼠标和键盘对用户界面进行操作,使表示层与业务逻辑层交互。
当业务逻辑层中的对象需要保存实现持久化时,就需要使用数据库实现对象的持久性,即保存对象中的数据。每个过程需要为每个逻辑类定义一个单独的数据访问层,以便处理数据和保存有用的信息。
提示
这3层构成了系统的物理模型。在构造系统模型过程中,开发人员会使用UML作为建造模型的工具。下节将介绍与此对应的3种模型。
1.5.5 面向对象的模型
UML中提供了3种面向对象的模型,使用这3种模型从不同的视角来描述系统,它们分别是描述系统内部对象及其关系的类模型,描述对象生命历史的状态模型,以及描述对象之间交互行为的交互模型。每种模型都会在开发的所有阶段中得到应用,并随着开发过程的进行获得更多的细节。对系统的完整描述,需要所有这3种视角的模型。
1.类模型
类模型(Class Model)描述了系统内部对象及其关系的静态结构。类模型界定了软件开发的上下文,包含类图。类图(Class Diagram)的节点是类,弧表示类间的关系。
2.状态模型
状态模型(State Model)描述了对象随着时间发生变化的那些方面。状态模型使用状态图确定并实现控制。状态图(State Diagram)的节点是状态,弧是由事件引发的状态间的转移。
3.交互模型
交互模型(Interaction Model)描述系统中的对象如何协作,以完成更广泛的任务。交互模型自用例开始,用例的概念随后会用顺序图和活动图详细描述。用例(Use Case)关注系统的功能,即系统为用户做了哪些事情。顺序图(Sequence Diagram)显示交互的对象以及发生交互的时间顺序。活动图(Activity Diagram)描述重要的处理步骤。
上述的3个模型描述了一套完整系统的相互独立的部分,但它们又是交叉相连的。类模型是最基本的,因为在描述何时以及如何发生变化之前,要先描述是哪些内容正在发生变化。