软件工程与UML案例解析(第三版)
上QQ阅读APP看书,第一时间看更新

3.4 面向对象软件开发技术

“对象”一词在现实生活中经常会遇到,它表示现实世界中的某个具体的事物,例如,一台计算机。现实世界中的事物包含了物质和意识两大部分,物质表达的是具体的事物,而意识则描述了某一个抽象的概念,是对客观存在的事物的一种概括。对象的概念源于用计算机程序对现实世界的复杂事物进行建模的过程。本节我们先来学习面向对象的一些基本概念,然后再了解面向对象开发的过程。

3.4.1 面向对象的基本概念

概念3-1:对象

解答:对象是指现实世界中各种各样的实体。它既可以是具体的能触及的事物,如一个人、一栋大楼、一架飞机,甚至一个地球等;也可以是无法触及的抽象事物,如一项计划、一次约会、一场演出等。一个对象既可以非常简单,又可以非常复杂,复杂的对象往往可以由若干个简单的对象组合而成。

扩展:对象定义了状态和行为。对象的状态用数据值来表示,而对象的行为则用来改变对象的状态。在面向对象的系统中,对象是基本的运行实体。

所有这些对象,除去它们都是现实世界中所存在的事物之外,它们都还具有各自不同的特征:

(1)有一个名字用来区别于其他对象。例如:张三、李四等。

(2)有一个状态用来描述对象的某些特征。例如:

对象张三的状态:

姓名:张三。

性别:男。

身高:179cm。

体重:71kg。

(3)有一组操作,每一个操作决定对象的一种功能或行为。对象的操作可分为两类:一类是自身所承受的操作,使用setter、getter作为操作名;一类是施加于其他对象的操作。例如:

对象张三的操作(可完成的功能):

回答姓名。

回答性别。

回答身高。

回答体重。

打电话。

踢足球。

驾车。

前四个操作属于对象自身所承受的操作,后三个则属于施加于其他对象的操作。

在这里我们理解了对象的概念、组成及特征,那么,展开思路想一想:具有相同特征的一组对象,情况又如何呢?这就是我们将要在下面讨论的类的问题。

概念3-2:类

解答:类是对一组客观对象的抽象,它将该组对象所具有的共同特征(包括结构特征和行为特征)集中起来,以说明该组对象的性质和能力。例如:“人”这个词就是抽象了所有人(单个的人,如张三、李四等,这些都是对象)的共同之处。

扩展:在面向对象编程中,通常把类定义为抽象数据类型,而对象是类的类型变量。当定义了一个类以后,就可以生成任意多个属于这个类的对象,这个过程叫作实例化。例如,用Java语言定义一个类Car为:

所生成的每个对象都与这个类相联系。那么,类与对象究竟有什么关系呢?

概念3-3:类与对象的关系

解答:组成类的对象均为该类的实例。类与对象之间的关系就是抽象与具体的关系。例如,张三是一个学生,学生是一个类,而张三作为一个具体的对象,是学生类的一个实例。类是多个实例的综合抽象,而实例又是类的个体事物。

扩展:在面向对象编程中,定义一个类就定义了这个类的一系列属性与操作,对于同一个类的不同实例之间,必定具有如下特点:

(1)相同的属性集合;

(2)相同的操作集合;

(3)不同的对象名。

相同的属性集合与操作集合用于标识这些对象属于同一个类,用不同的对象名来区别不同的对象。

概念3-4:面向对象

解答:面向对象(Object-Oriented,OO)是指人们按照自然的思维方式认识客观世界,采用基于对象(实体)的概念建立模型,模拟客观世界,从而用来分析、设计和实现软件的方法。把软件组织成一系列离散的、合并了数据结构和行为的对象集。

扩展:面向对象是当前计算机界关注的重点,它是20世纪90年代以来的主流软件开发方法。通过面向对象的理念使计算机软件系统能与现实世界中的系统一一对应。面向对象的概念和应用已超越了程序设计和软件开发,扩展到很宽的范围,如数据库系统、分布式系统、人工智能等领域。

采用面向对象技术开发软件的方法,称为面向对象软件开发方法。面向对象方法具有以下几个特性:

(1)对象唯一性

每个对象都有唯一的标识(如同居民身份证号码),通过这个标识,可以找到相应的对象。在对象的整个生命周期(从对象的创建到对象的消亡)中,它的标识都不会改变。不同的对象(即使其他属性完全相同,例如两个完全一样的球)必须具有不同的标识。

(2)封装性

具有一致的数据结构(属性)和行为(操作)的对象抽象成类。

(3)继承性

子类自动共享父类的数据结构和行为的机制,这是类之间的一种关系。继承性是面向对象程序设计语言不同于其他程序设计语言最重要的特点,是其他语言所没有的。

(4)多态性

它是面向对象系统中的又一个重要特性。多态性描述的是同一个消息可以根据发送消息对象的不同采用多种不同的行为方式,即指相同的操作或函数可作用于多种类型的对象上并获得不同的结果。不同的对象,收到同一消息可以产生不同的结果,这种现象就称为多态性。

在下面的内容中将对面向对象系统最突出的三大特性:封装性、继承性和多态性做更详细的描述。

概念3-5:封装及面向对象系统的封装性

解答:封装也叫作信息隐藏。从字面上理解,封装就是将某事物包装起来,使外界不了解其实际内容。从软件开发的角度,封装是指将一个数据和与这个数据有关的各种操作放在一起,形成一个能动的实体——对象,使用者不必知道对象的内部结构,只需根据对象提供的外部接口访问对象。从使用者的角度看,这些对象就像一个“黑盒子”,其内部数据和行为是被隐藏起来、看不见的。

面向对象系统的封装性是一种信息隐藏技术,它隐藏了某一方法的具体执行步骤,取而代之的是通过消息传递机制传递消息给它。

扩展:封装的意义在于保护或者防止数据或者程序代码被我们无意中破坏,以提高系统的安全性。既然封装了对象,那么,怎样才能访问对象呢?答案是通过对象提供的公共消息接口来访问对象。下面是一个用Java语言所定义的类:

在上面所定义的职员(Employee)类中,包含的数据有职员姓名(name)、年龄(age)、工资(salary)、职位(rank)。这些数据被定义为私有数据(用private定义),就被隐藏起来,对于外部对象来说就不可以直接访问了。它所包含的成员方法或成员函数(操作)有三种类型的可见性:

(1)私有的。用private关键字标识,如calculateSalary,这样的成员方法或函数不向外部公开,只供对象内部自己使用。

(2)受保护的。用protected关键字标识,如getAge,这样的成员方法或函数只向部分外界公开,只对派生类对象提供服务。

(3)公有的。用public关键字标识,如getSalary等,这样的成员方法或函数向所有的外界公开,它可以响应外界对象的请求。

可以看出,定义为公有的成员方法或函数,才是对象提供的公共消息接口。外部对象只有通过这些公共接口才能访问到对象的内部数据,这就是所谓的信息隐藏技术。

根据对封装的定义可以看出,封装应该具有下面几个条件:

(1)具有一个清晰的边界。对象所有的私有数据、成员方法或函数的实现细节都被固定在这个边界内。

(2)具有一个接口。这个接口描述了对象之间的交互作用,它就是消息。

(3)对象内部的实现代码受到封装体的保护,其他对象不能直接修改本对象所拥有的数据和代码。

面向对象系统中的封装以对象为单位,即主要是指对象的封装,该对象的特性是由它所属的类说明来描述,也就是说在类的定义中实现封装。被封装的对象通常被称为抽象数据类型。封装性提高了对象内部数据的安全性。

概念3-6:继承及面向对象系统的继承性

解答:继承是指一个类能够从另一个类那里获得一些特性。在这个过程中,超类把它的特性赋给了子类。

面向对象系统的继承性是对具有层次关系的类的属性和操作进行共享的一种方式。在面向对象系统中,若没有引入继承的概念,所有的类就会变成一盘各自为政、彼此独立的散沙,软件重用级别较低,每次软件开发就只能从“零”开始。

扩展:继承是面向对象软件技术中的一个概念。如果一个类A继承自另一个类B,就把这个A称为B的“子类”或“派生类”,而把B称为A的“父类”或“超类”或“基类”。例如,在通常的信息管理应用系统中,都会涉及用户权限管理,常常会有“一般用户”和“系统管理员”两种角色,而“一般用户”和“系统管理员”都是“用户”,所以,“一般用户”类和“系统管理员”类都可以继承自“用户”类。在这里,“一般用户”类和“系统管理员”类都是“用户”类的子类,而“用户”类则是“一般用户”类和“系统管理员”类的父类。

继承可以使得子类具有父类的各种属性和方法,而不需要再次编写相同的代码。在子类继承父类时,既可以重新定义子类的某些属性和方法,也可以重写某些方法,来覆盖父类的原有属性和方法,使其获得与父类不同的功能。

继承有两个方面的作用:①避免代码冗余,提高可理解性和可维护性;②继承是从老对象生成新对象的一种代码重用机制,使系统更具灵活性和适应性,它使得解释多态性成为可能。

继承有单继承和多继承之分。图3-6所示为类间的单继承关系。

从图3-6中可以看出,椭圆形类、矩形类和三角形类是图形类的子类(或派生类),图形类是椭圆形类、矩形类和三角形类的父类(或超类、基类);圆形类是椭圆形类的子类,椭圆形类是圆形类的父类;正方形类和长方形类是矩形类的子类,矩形类是正方形类和长方形类的父类。由以上这些类所组成的层次结构是一种单继承的派生方式,即每个子类只是继承了一个父类的特性。

使用单继承可以解决许多问题,但在很多情况下,需要不同形式的继承才能解决问题,单继承就显得无能为力了。例如,用户界面常常要提供对话框、文本框、列表框、组合框、复选框以及各种类型的按钮。假如这些都是通过类来完成的,如果要把所有这些类型合并成一个新类型,这样,就产生了多继承的概念。所谓多继承就是在子类中继承了一个以上父类的属性。如图3-7所示。

图3-6 单继承

图3-7 多继承

组合框类同时继承了文本框类和列表框类的属性,它有两个父类,对话框类也同时继承了五个类的属性,具有五个基类,这是一个多继承的类层次的例子。

多重继承的引入,使面向对象系统大大增加了模拟现实世界的能力,但是系统结构变得非常复杂,增加了系统的理解与维护难度。在面向对象程序设计语言中,C++支持多继承,而Java语言只支持单继承,不支持多继承。

在面向对象开发中,继承性不仅作用在对操作的继承,还作用在对数据的继承,也就是说,既具有结构特性的继承性,又具有行为特性的继承性。子类是否可以访问父类的所有成员变量和成员方法(或成员函数)?在前面介绍封装性时谈到,定义类的数据成员与方法成员有三种访问域(三种可见性),即公有访问域(public)、受保护访问域(protected)、私有访问域(private)。父类的成员若定义为受保护访问域和公有访问域,子类是可以访问的;若父类成员定义为私有访问域,子类则无权访问。

概念3-7:重载及面向对象系统的多态性

解答:一个类中的操作具有相同的名称和不同的参数,这样的操作被称为“重载”。面向对象系统的多态性是指,当不同的对象收到相同的消息时产生不同的动作。常用在功能相同但参数的数据类型有微小差别的操作中。

3.4.2 面向对象的开发

问题引入

传统的软件开发生命周期包含了六个阶段:制订计划、需求分析和定义、设计、编码、测试、运行与维护,面向对象的开发和传统的软件开发方法不同,它是一种基于现实世界抽象的新的软件开发方法。那么,面向对象的软件开发中,软件生命周期又可分为哪几个阶段?各个阶段主要完成哪些任务?

扫一扫 看视频

解答问题

在面向对象的软件开发中,软件生命周期可分为以下几个阶段:

(1)系统分析阶段

在这个阶段,需要建立一个反映现实客观世界情形的模型。为了建立这个模型,需要分析员和需求人员共同明确现实世界中的问题。这个模型应该解决系统必须做什么,而不是怎么做的问题。分析之后将得到分析模型:对象模型、动态模型和功能模型。

(2)系统设计阶段

这个阶段需要给出怎样解决问题的决策,包括将系统划分成子系统和子系统软硬件如何配置,确定系统的整体框架结构。

(3)对象设计阶段

该阶段将应用领域的概念转换为计算机软件领域的概念。在系统分析阶段所定义的问题,在这个阶段来确定解决问题的方法。将分析模型的逻辑结构映射到一个程序的物理组织,得到设计模型。

(4)实现阶段

在这个阶段,将在对象设计阶段开发的类转换成用特定的程序设计语言编写的代码或数据库。

(5)测试阶段

传统软件开发的测试通常经过单元测试、集成测试、系统测试三个环节。由于面向对象有其自身的特点,参考面向对象软件开发模式,面向对象开发测试包括面向对象分析的测试、面向对象设计的测试、面向对象编程的测试、面向对象单元测试、面向对象集成测试和面向对象系统测试。

分析问题

总的看来,面向对象开发同样包含了传统软件开发的几个阶段:分析、设计、编码、测试等,但面向对象开发是用“面向对象”的观点去认识客观世界,用“面向对象”的方法去模拟客观世界,所以,在分析阶段主要分析现实世界中的对象以及对象与对象之间的关系,在设计阶段除了对整个系统的架构设计外,主要针对对象(或类)以及对象(或类)与对象(或类)之间的关系进行设计,在编码阶段使用面向对象编程语言实现类的各项功能,而在测试阶段则使用面向对象的测试方法与技术。

人们对客观世界的认识是一个从简单到复杂、从知之不多到知之甚多的反复的认识过程,因此,面向对象的软件开发也将是一个反复的迭代过程,但这种反复不是简单的重复,而是在前一个迭代周期基础上对于问题领域的认识有所提高。

结论:面向对象开发生命周期是一个迭代的增量式过程,因此,使用面向对象软件开发技术能很好地适应系统需求的变化,并能提高软件的可理解性、可维护性和可重用性,以至于提高软件企业的可持续发展性。

面向对象的分析与设计过程适合于使用UML来建模,同时,使用UML建模工具可以将模型(对于嵌入式系统,通常指状态图;而对于一般系统,则是指类图)映射为特定编程语言的程序代码,也可以将永久类映射为关系数据库结构,还能将分析模型中得到的边界类的属性与操作部分映射为用户界面中的图形元素。详细的建模过程详见后面的章节,这里不再叙述。面向对象开发特别适合采用下一节将要介绍的RUP统一软件开发过程。