第9章 面向对象方法学引论
一、面向对象方法学概述
1.要点
面向对象方法学已经成为人们在开发软件时首选的范型。面向对象技术已成为当前最好的软件开发技术。
(1)基本原则
面向对象方法学的出发点和基本原则,是尽可能模拟人类习惯的思维方式,使开发软件的方法与过程尽可能接近人类认识世界解决问题的方法与过程,使描述问题的问题空间(问题域)与实现解法的解空间(求解域)在结构上尽可能一致。
(2)定义
面向对象方法是一种以数据或信息为主线,把数据和处理相结合的方法,即把对象作为由数据及可以施加在这些数据上的操作所构成的统一体。面向对象的方法可以用下列方程来概括:
OO = object + classes + inheritance + communication with messages
(3)要点
①对象
面向对象的软件系统是由对象组成的,软件中的任何元素都是对象,复杂的软件对象由比较简单的对象组合而成。用对象分解取代了传统方法的功能分解,对象是从客观世界中的实体抽象而来的,是不固定的
②类
把所有对象都划分成各种对象类,每个对象类都定义了一组数据和一组方法。数据用于表示对象的静态属性,是对象的状态信息。类中定义的方法,是允许施加于该类对象上的操作,是该类所有对象共享的,并不需要为每个对象都复制操作的代码。
③继承性
按子类与父类的关系,把若干个对象类组成一个层次结构的系统。子类自动具有和上层的父类相同的数据和方法,而且低层的特性将屏蔽高层的同名特性。
④封装性
对象彼此之间仅能通过传递消息互相联系。对象是进行处理的主体,必须发消息请求它执行它的某个操作,处理它的私有数据,而不能从外界直接对它的私有数据进行操作。一切局部于该对象的私有信息,都被封装在该对象类的定义中,就好像装在一个不透明的黑盒子中一样,在外界是看不见的,更不能直接使用,
2.面向对象方法学的优点
(1)与人类习惯的思维方法一致
①以对象为核心,开发出的软件系统由对象组成。
②设计的主要思路是使用现实世界的概念抽象地思考问题从而自然地解决问题。
③基本原则是按照人类习惯的思维方法建立问题域和求解域模型。
④抽象机制使用户在利用计算机软件系统解决复杂问题时使用习惯的抽象思维工具。
⑤对象分类过程,支持从特殊到一般的归纳思维过程。
⑥继承特性,支持从一般到特殊的演绎思维过程。
⑦提供了随着对系统认识的逐步深入和具体化,而逐步设计和实现该系统的可能性。
(2)稳定性好
面向对象方法基于构造问题领域的对象模型,以对象为中心构造软件系统。它的基本作法是用对象模拟问题领域中的实体,以对象间的联系刻画实体间的联系。因为面向对象的软件系统的结构是根据问题领域的模型建立起来的,而不是基于对系统应完成的功能的分解,当对系统的功能需求变化时不会引起软件结构的整体变化,仅需要作一些局部性的修改。
(3)可重用性好
①定义
重用技术是用已有的零部件装配新的产品。重用是提高生产率的最主要的方法。对象是比较理想的模块和可重用的软件成分。
②方法
a.创建该类的实例,从而直接使用它。
b.从该类派生出一个满足当前需要的新类。
(4)较易开发大型软件产品
面向对象方法学开发软件时,把一个大型软件产品分解成一系列本质上相互独立的小产品来处理,不仅降低了开发的技术难度,而且使开发工作的管理变容易了。
(5)可维护性好
用传统方法和面向过程语言开发出来的软件很难维护,是长期困扰人们的一个严重问题,是软件危机的突出表现。下面几个因素使得面向对象方法所开发的软件可维护性好:
①面向对象的软件稳定性比较好。
②面向对象的软件比较容易修改。
③面向对象的软件比较容易理解。
④易于测试和调试。
二、面向对象的概念
1.对象
(1)对象的形象表示
“对象”是面向对象方法学中使用的最基本的概念,对象的形象表示如图9-1所示。
图9-1 对象的形象表示
图9-1形象地描绘了具有3个操作的对象。面向对象方法学中的对象是由描述该对象属性的数据以及可以对这些数据施加的所有操作封装在一起构成的统一体。对象可以作的操作表示它的动态行为。
(2)对象的定义
①面向对象程序设计的角度
对象是具有相同状态的一组操作的集合。在应用领域中有意义的、与所要解决的问题有关系的任何事物都可以作为对象,它既可以是具体的物理实体的抽象,也可以是人为的概念,或者是任何有明确边界和意义的东西。
②结构化的角度
对象是封装了数据结构及可以施加在这些数据结构上的操作的封装体,这个封装体有可以唯一地标识它的名字,而且向外界提供一组服务。对象中的数据表示对象的状态,一个对象的状态只能由该对象的操作来改变。每当需要改变对象的状态时,只能由其他对象向该对象发送消息。对象响应消息时,按照消息模式找出与之匹配的方法,并执行该方法。
③动态角度(对象的实现机制)
图9-2 用自动机模拟对象
对象是一台自动机。具有内部状态S,操作fi(i=1,2,···,n),且与操作fi对应的状态转换函数为gi(i=1,2,···,n)的一个对象,可以用图9-2所示的自动机来模拟。
(3)对象的特点
①以数据为中心
操作围绕对其数据所需要做的处理来设置,不设置与这些数据无关的操作,而且操作的结果往往与当时所处的状态有关。
②对象是主动的
对象进行处理的主体。不能从外部直接加工它的私有数据,而是必须通过它的公有接口向对象发消息,请求它执行它的某个操作,处理它的私有数据。
③实现了数据封装
私有的数据完全被封装在内部,对外是隐藏的、不可见的,对私有数据的访问或处理只能通过公有的操作进行。为了使用对象内部的私有数据,只需知道数据的取值范围和可以对该数据施加的操作。一个对象类型也可以看作是一种抽象数据类型。
④具有并行性
对象是描述其内部状态的数据及可以对这些数据施加的全部操作的集合。不同对象各自独立地处理自身的数据,彼此通过发消息传递信息完成通信。因此,本质上具有并行工作的属性。
⑤模块独立性好
对象是由数据及可以对这些数据施加的操作所组成的统一体,而且对象是以数据为中心的,操作围绕对其数据所需做的处理来设置,没有无关的操作。因此,对象内部各种元素彼此结合得很紧密,内聚性相当强。它与外界的联系比较少,对象之间的耦合比较松。
2.其他概念
(1)类
“类”是对具有相同数据和相同操作的一组相似对象的定义,即类是对具有相同属性和行为的一个或多个对象的描述,包括对怎样创建该类的新对象的说明。类是支持继承的抽象数据类型,而对象就是类的实例。
(2)实例
实例就是由某个特定的类所描述的一个具体的对象。类是对具有相同属性和行为的一组相似的对象的抽象,类在现实世界中并不能真正存在。实际上类是建立对象时使用的“样板”,按照这个样板所建立的一个个具体的对象,就是类的实际例子,通常称为实例。
(3)消息
消息就是要求某个对象执行在定义它的那个类中所定义的某个操作的规格说明。一个消息由接收消息的对象、消息选择符、零个或多个变元组成。
(4)方法
方法就是对象所能执行的操作,也就是类中所定义的服务。方法描述了对象执行操作的算法,响应消息的方法。
(5)属性
属性就是类中所定义的数据,它是对客观世界实体所具有的性质的抽象。类的每个实例都有自己特有的属性值。
(6)封装
①定义
封装是把数据和实现操作的代码集中起来放在对象内部。封装也就是信息隐藏,通过封装对外界隐藏了对象的实现细节。
②特点
a.有一个清晰的边界
有一个清晰的边界。所有私有数据和实现操作的代码都被封装在这个边界内,从外面看不见更不能直接访问。
b.有确定的接口(即协议)
这些接口就是对象可以接受的消息,只能通过向对象发送消息来使用它。
c.受保护的内部实现
实现对象功能的细节(私有数据和代码)不能在定义该对象的类的范围外访问。
注意:对象类实质上是抽象数据类型。类把数据说明和操作说明与数据表达和操作实现分离开了,使用者只需知道它的说明,就可以使用它。
(7)继承
①定义
广义地说,继承是指能够直接获得已有的性质和特征,而不必重复定义它们。在面向对象的软件技术中,继承是子类自动地共享基类中定义的数据和方法的机制。
②原理
图9-3 实现继承机制的原理
图9-3中以A、B两个类为例,B类是从A类派生出来的子类,它除了具有自己定义的特性之外,还从父类A继承特性。创建A类的实例a1时,a1以A类为样板建立实例变量,并不从A类中复制所定义的方法。创建B类的实例b1的时候,b1既要以B类为样板建立实例变量,又要以A类为样板建立实例变量,b1所能执行的操作既有B类中定义的方法,又有A类中定义的方法,这就是继承。
③特点
a.继承具有传递性;
b.低层的性质将屏蔽高层的同名性质。
④分类
a.单继承:一个类只允许有一个父类,即类等级为树形结构。
b.多重继承:允许一个类有多个父类。
⑤实现软件重用
a.使用抽象的类开发出一般性问题的解;
b.在派生类中增加少量代码使一般性的解具体化。
(8)多态性
①定义
多态性是指子类对象可以像父类对象那样使用,同样的消息既可以发送给父类对象也可以发送给子类对象。即在类等级的不同层次中可以共享一个方法的名字,不同层次中的每个类各自按自己的需要来实现这个行为。
②动态联编
在运行时刻根据接收消息的对象所属于的类,决定执行哪个特定的版本,这称为动态联编,也叫滞后联编。
③优点
a.增加了面向对象软件系统的灵活性,进一步减少了信息冗余。
b.显著提高了软件的可重用性和可扩充性。
(9)重载
①分类
a.函数重载
在同一作用域内的若干个参数特征不同的函数可以使用相同的函数名字。
b.运算符重载
同一个运算符可以施加于不同类型的操作数上面。
当参数特征不同或被操作数的类型不同时,实现函数的算法或运算符的语义是不相同的。
②实现
a.函数重载是通过静态联编(先前联编)实现的。
b.运算符重载是在编译时根据被操作数的类型,决定使用该算符的哪种语义。
③优点
进一步提高了面向对象系统的灵活性和可读性。
三、面向对象建模
1.模型
(1)定义
模型是为了理解事物而对事物作出的一种抽象,是对事物的一种无歧义的书面描述。模型由一组图示符号和组织这些符号的规则组成,利用它们来定义和描述问题域中的术语和概念。
(2)作用
①模型是一种思考工具,利用这种工具可以把知识规范地表示出来。
②模型可以帮助人们思考问题、定义术语、在选择术语时作出适当的假设,并且有助于保持定义和假设的一致性。
2.建模的重要性
由于建模忽略了事物的非本质属性,因此模型比原始事物更容易操作。对于那些因过分复杂而不能直接理解的系统,特别需要建立模型,模型通过把系统的重要部分分解成人的头脑一次能处理的若干个子部分,从而减少了系统的复杂程度。
3.模型分类
(1)对象模型:描述系统数据结构。
(2)动态模型:描述系统控制结构。
(3)功能模型:描述系统功能。
这3种模型都涉及数据、控制和操作等共同的概念,只不过每种模型描述的侧重点不同。这3种模型从3个不同但又密切相关的角度模拟目标系统,它们各自从不同侧面反映了系统的实质性内容,综合起来则全面地反映了对目标系统的需求。
四、对象模型
1.概念
(1)定义
对象模型表示静态的、结构化的系统的数据性质。它是对模拟客观世界实体的对象以及对象彼此间的关系的映射,描述了系统的静态结构。对象模型为建立动态模型和功能模型,提供了实质性的框架。
(2)工具
使用UML提供的类图来建立对象模型。在UML中,类的实际含义是一个类及属于该类的对象。
2.类图的基本符号
类图描述类及类与类之间的静态关系。类图是一种静态模型,它是创建其他UML图的基础。一个系统可以由多张类图来描述,一个类也可以出现在几张类图中。
(1)定义类
①表示
图9-4 表示类的图形符号
UML中类的图形符号为长方形,用两条横线把长方形分戒上、中、下3个区域,3个区域分别放类的名字、属性和服务,如图9-4所示。
②命名规则
类名是一类对象的名字。命名是否恰当对系统的可理解性影响相当大,因此,为类命名时应该遵守以下几条准则:
a.使用标准术语。应该使用在应用领域中人们习惯的标准术语作为类名,不要随意创造名字。
b.使用具有确切含义的名词。尽量使用能表示类的含义的13常用语作名字,不要使用空洞的或含义模糊的词作名字。
c.必要时用名词短语作名字。为使名字的含义更准确,必要时用形容词加名词或其他形式的名词短语作名字。
注意:类名应该是富于描述的、简洁的而且无二义性的。
(2)定义属性
UML描述属性的语法格式如下:
可见性 属性名:类型名=初值{性质串}
①可见性
属性的可见性分为公有的(+)、私有的(-)和保护的(#),如果未声明可见性,则表示该属性的可见性尚未定义。
注意:没有默认的可见性。
②类型名
表示该属性的数据类型,它可以是基本数据类型或是是用户自定义的类型。属性名和类型名之间用冒号(:)分隔。
③赋值
在创建类的实例时应给其属性赋值,如果给某个属性定义了初值,则该初值可作为创建实例时这个属性的默认值。类型名和初值之间用(=)隔开。
④性质串
性质串明确地列出该属性所有可能的取值,不同值之间用逗号隔开。性质串用花括号({})括起来。
(3)定义服务
服务也就是操作,UML描述操作的语法格式如下:
可见性 操作名(参数表):返回值类型{性质串}
①可见性
操作的可见性分为公有的(+)、私有的(-)和保护的(#),如果未声明可见性,则表示该操作的可见性尚未定义。
注意:没有默认的可见性。
②参数表
用逗号分隔的形式参数的序列。描述一个参数的语法如下:
参数名:类型名=默认值
在类中也可以定义类作用域操作,在类图中表示带下划线的操作。这类操作只能存取本类的类作用域属性。
3.表示关系的符号
类与类之间通常有关联、泛化(继承)、依赖和细化4种关系。
(1)关联
①定义
关联表示两个类的对象之间存在某种语义上的联系。
②关联的角色
图9-5 关联的角色
在任何关联中都会涉及参与此关联的对象所扮演的角色,在某些情况下显式标明角色名有助于别人理解类图。如图9-5所示。如果没有显式标出角色名,则意味着用类名作为角色名。
③普通关联
a.定义
普通关联是最常见的关联关系,只要在类与类之间存在连接关系就可以用普通关联表示。
b.表示
图9-6 普通关联示例
第一,普通关联的图示符号是连接两个类之间的直线,如图9-6所示。
第二,关联是双向的,可为关联起一个名字。在名字前面(或后面)加一个表示关联方向的黑三角。
第三,在表示关联的直线两端可以写上重数,它表示该类有多少个对象与对方的一个对象连接。未明确标出关联的重数,则默认重数是1。
④限定关联
a.定义
限定关联通常用在一对多或多对多的关联关系中,可以把模型中的重数从一对多变成一对一,或从多对多简化成多对一。
b.表示
图9-7 一个受限的关联
在类图中把限定词放在关联关系末端的一个小方框内。如图9-7所示,利用限定词“文件名”表示了目录与文件之间的关系,利用限定词把一对多关系简化成了一对一关系。
c.意义
限定提高了语义精确性,增强了查询能力。
⑤关联类
a.定义
为了说明关联的性质,可能需要一些附加信息。关联类可以用来记录相关信息。
b.表示
图9-8 关联类示例
如图9-8所示。关联中的每个连接与关联类的一个对象相联系。关联类通过一条虚线与关联连接。
(2)聚集
聚集(聚合)是关联的特例。表示类与类之间的关系是整体与部分的关系。在陈述需求时使用的“包含”、“组成”、“分为……部分”等字句,往往意味着存在聚集关系。除了一般聚集之外,还有两种特殊的聚集关系,分别是共享聚集和组合聚集。
①共享聚集
a.定义
如果在聚集关系中处于部分方的对象可同时参与多个处于整体方对象的构成,则该聚集称为共享聚集。
b.表示
图9-9 共享聚集示例
如图9-9所示。一般聚集和共享聚集的图示符号,都是在表示关联关系的直线末端紧挨着整体类的地方画一个空心菱形。
②组合聚集
a.定义
如果部分类完全隶属于整体类,部分与整体共存,整体不存在了部分也会随之消失,则该聚集称为组合聚集(组成)。
b.表示
图9-10 组合聚集示例
如图9-10所示,组成关系用实心菱形表示。
(3)泛化
UML中的泛化关系就是继承关系,它是通用元素和具体元素之间的一种分类关系。具体元素完全拥有通用元素的信息,并且还可以附加一些其他信息。在UML中,用一端为空心三角形的连线表示泛化关系,三角形的顶角紧挨着通用元素。
①普通泛化
a.抽象类
图9-11 抽象类示例
没有具体对象的类称为抽象类。抽象类通常都有抽象操作,作用来指定该类的所有子类应具有哪些行为。如图9-11所示,表示抽象类是在类名下方附加一个标记值{abstract},表示抽象操作是在操作标记后面跟随一个性质串{abstract}。
b.具体类
图9-12 复杂类图示例
具体类有自己的对象,并且该类的操作都有具体的实现方法。如图9-12所示,是一个比较复杂的类图示例。
②受限泛化
a.定义
可以给泛化关系附加约束条件,以进一步说明该泛化关系的使用方法或扩充方法,这样的泛化关系称为受限泛化。
b.约束
预定义的约束有4种:多重、不相交、完全和不完全。这些约束都是语意约束。
c.继承分类
第一,多重继承
一个子类可以同时多次继承同一个上层基类,如图9-13所示。
图9-13 多重继承示例
第二,不相交继承
一个子类不能多次继承同一个基类。一般的继承都是不相交继承。
第三,完全继承
父类的所有子类都已在类图中穷举出来了。
第四,不完全继承
父类的子类并没有都穷举出来,随着对问题理解的深入,可不断补充和维护。是默认的继承关系。
(4)依赖和细化
①依赖关系
a.定义
依赖关系描述两个模型元素之间的语义连接关系:其中一个模型元素是独立的,另一个模型元素不是独立的,它依赖于独立的模型元素,如果独立的模型元素改变了,将影响依赖于它的模型元素。
b.表示
图9-14 友元依赖关系
如图9-14所示,在UML类图中用带箭头的虚线连接有依赖关系的两个类,箭头指向独立的类。在虚线上可以带一个版类标签,具体说明依赖的种类。
②细化关系
a.定义
对同一个事物在不同抽象层次上描述时,这些描述之间具有细化关系。
b.表示
图9-15 细化关系示例
如图9-15所示,细化的图示符号为由元素B指向元素A的一端为空心三角形的虚线。
c.适用性
细化用来协调不同阶段模型之间的关系,表示各个开发阶段不同抽象层次的模型之间的相关性,常常用于跟踪模型的演变。
五、动态模型
1.概念
动态模型表示瞬时的、行为化的系统的控制性质,它规定了对象模型中的对象的合法变化序列。
2.建模
用UML提供的状态图来描绘对象的状态、触发状态转换的事件以及对象的行为。每个类的动态行为用一张状态图来描绘,各个类的状态图通过共享事件合并起来,从而构成系统的动态模型,即动态模型是基于事件共享而互相关联的一组状态图的集合。
六、功能模型
1.概念
(1)定义
功能模型表示变化的系统的功能性质,它指明了系统应该做什么,因此更直接地反映了用户对目标系统的需求。
(2)组成
功能模型由一组数据流图组成。
2.用例图
UML提供的用例图也是进行需求分析和建立功能模型的强有力工具。在UML中把用用例图建立起来的系统模型称为用例模型。
(1)定义
用例模型描述的是外部行为者所理解的系统功能。用例模型的建立是系统开发者和用户反复讨论的结果,它描述了开发者和用户对需求规格所达成的共识。
(2)表示
图9-16 自动售货机系统用例图
图9-16是自动售货机系统的用例图。
①系统
a.定义
系统被看作是一个提供用例的黑盒子,内部如何工作、用例如何实现,这些对于建立用例模型来说都是不重要的。
b.表示
系统用方框表示,其边线表示系统的边界,用于划定系统的功能范围,定义了系统所具有的功能。描述该系统功能的用例置于方框内,代表外部实体的行为者置于方框外。
②用例
a.定义
一个用例是可以被行为者感受到的、系统的一个完整的功能。在UML中把用例定义成系统完成的一系列动作。
b.表示
在UML中,椭圆代表用例。用例通过关联与行为者连接,关联指出一个用例与哪些行为者交互,这种交互是双向的。
c.特征
第一,用例代表某些用户可见的功能,实现一个具体的用户目标。
第二,用例总是被行为者启动的,并向行为者提供可识别的值。
第三,用例必须是完整的。
注意:用例是一个类,它代表一类功能而不是使用该功能的某个具体实例。用例的实例是系统的一种实际使用方法,通常把用例的实例称为脚本。脚本是系统的一次具体执行过程。
③行为者
a.定义
行为者是指与系统交互的人或其他系统,它代表外部实体。使用用例并且与系统交互的任何人或物都是行为者。行为者代表一种角色,而不是某个具体的人或物。
b.表示
在UML中,线条人代表行为者。在用例图中用直线连接行为者和用例,表示两者之间交换信息,称为通信联系。行为者触发用例,并与用例交换信息。单个行为者可与多个用例联系,一个用例也可与多个行为者联系。
④用例间的关系
UML用例之间主要有扩展和使用两种关系,它们是泛化关系的两种不同形式。
a.扩展关系
向一个用例中添加一些动作后构成了另一个用例,这两个用例之间的关系就是扩展关系,后者继承前者的一些行为,通常把后者称为扩展用例。如图9-17所示,用例之间的扩展关系图示为带版类《扩展》的泛化关系。
图9-17 含扩展和使用关系的用例图
b.使用关系
一个用例使用另一个用例时,这两个用例之间就构成了使用关系。如图9-17所示,用例之间的使用关系用带版类《使用》的泛化关系表示。
c.两种关系的异同
第一,都是从几个用例中抽取那些公共的行为并放入一个单独的用例中,而这个用例被其他用例使用或扩展。
第二,使用和扩展的目的是不同的。在描述一般行为的变化时采用扩展关系。
第三,在两个或多个用例中出现重复描述又想避免这种重复时,采用使用关系。
3.用例建模
(1)创建用例模型
用例集中的每个用例都是对系统的一个潜在的需求,一个用例模型由若干幅用例图组成。创建用例模型的工作包括:定义系统,寻找行为者和用例,描述用例,定义用例之间的关系,确认模型。其中,寻找行为者和用例是关键。
(2)寻找行为者和用例
①寻找行为者
为获取用例首先要找出系统的行为者,可以通过请系统的用户回答一些问题的办法来发现行为者。
②寻找用例
一旦找到了行为者,就可以通过请每个行为者回答下述问题来获取用例:
a.行为者需要系统提供哪些功能?行为者自身需要做什么?
b.行为者是否需要读取、创建、删除、修改或存储系统中的某类信息?
c.系统中发生的事件需要通知行为者吗?行为者需要通知系统某些事情吗?从功能观点看,这些事件能做什么?
d.行为者的日常工作是否因为系统的新功能而被简化或提高了效率?
七、三种模型比较
1.三种模型
(1)三种模型相互补充、相互配合,使得人们对系统的认识更加全面。
(2)对象模型是最基本最重要的,它为其他两种模型奠定了基础。
2.关系
(1)针对每个类建立的动态模型,描述了类实例的生命周期或运行周期。
(2)状态转换驱使行为发生,这些行为在数据流图中被映射成处理,在用例图中被映射成用例,它们同时与类图中的服务相对应。
(3)功能模型中的处理对应于对象模型中的类所提供的服务。
(4)数据流图中的数据存储,以及数据的源点/终点,通常是对象模型中的对象。
(5)数据流图中的数据流,往往是对象模型中对象的属性值,也可能是整个对象。
(6)用例图中的行为者,可能是对象模型中的对象。
(7)功能模型中的处理可能产生动态模型中的事件。
(8)对象模型描述了数据流图中的数据流、数据存储以及数据源点/终点的结构。