1、第 7 章 面向对象的分析和设计面向对象方法是一种把面向对象的思想应用于软件开发过程中,指导开发活动的系统方法,简称 OO 方法,它是建立在对象概念(对象、类和继承)基础上的方法。 1960 年代后期出现了面向对象的编程语言,在 Simula-67 语言中引入了类和对象的概念,1970 年代初 Xerox 公司推出了 Smalltalk 语言,奠定了面向对象程序设计的基础,1980 年出现的Smalltalk-80 标志着面向对象程序设计进入了实用阶段。自八十年代中期起,人们注重于面向对象分析和设计的研究,逐步形成了面向对象方法学。典型的方法有 P.Coad 和E.Yourdon 的面向对象分
2、析(OOA)和面向对象设计(OOD ),G.Booch 的面向对象开发方法,J.Rumbaugh 等人提出的对象建模技术( OMT),Jacobson 的面向对象软件工程(OOSE )等。1990 年代中期,由 G.Booch,J.Rumbaugh,Jacobson 等人发起,在 Booch方法、OMT 方法和 OOSE 方法的基础上推出了统一的建模语言(UML ),1997 年被国际对象管理组织(OMG)确定作为标准的建模语言。面向对象方法的出现很快受到计算机软件界的青睐,并成为 1990 年代的主流开发方法。我们可以从下列几个方面来分析其原因:1. 从 认 知 学 的 角 度 来 看 ,面
3、 向 对 象 方 法 符 合 人 们 对 客 观 世 界 的 认 识 规 律 。很长一段时间里,我们分析、设计、实现一个软件系统的过程与我们认识一个系统的过程存在着差异。例如结构化方法分析的结果是数据流图,设计的结果是模块结构图,实现的结果是由程序模块组成的源程序。这些图中的成分或程序模块不能直接映射到客观世界中系统的实体上,也就是说,解空间的结构与问题空间的结构是不一致的。当用户需求有一些小的改变时,这种不一致性将导致分析、设计的较大变化。而面向对象方法则以客观世界中系统的实体为基础,将客观实体的属性及其操作封装成对象。在分析阶段,识别系统中的对象以及它们之间的关系;在设计阶段,仍延用分析的
4、结果,并根据实现的需要增加、删除或合并某些对象,或在某些对象中添加相关的属性和操作,同时设计实现这些操作的方法;在实现阶段,则用程序设计语言来描述这些对象以及它们之间的联系。因此,面向对象方法的分析、设计、实现的结果能直接映射到客观世界中系统的实体上,也就是说,解空间的结构与问题空间的结构是一致的。分析、设计、实现一个系统的过程与认识这个问题的过程是一致的。由于面向对象的分析和设计采用同样的图形表示形式,分析、设计和实现都以对象为基础,因此面向对象开发的各阶段之间具有很好的无缝连接。当用户的需求有所改变时,由于客观世界中的实体是不变的,实体之间的联系也是基本不变的,因此面向对象的总体结构也相对
5、比较稳定,所引起的变化大多集中在对象的属性与操作及对象之间的消息通信上。总之,面向对象方法符合人们对客观世界的认识规律,所开发的系统相对比较稳定。2. 面 向 对 象 方 法 开 发 的 软 件 系 统 易 于 维 护 ,其 体 系 结 构 易 于 理 解 、扩 充 和 修改 。面向对象方法开发的软件系统由对象类组成,对象的封装性很好地体现了抽象和信息隐蔽的特征。对象以属性及操作为接口,使用者只可通过接口访问对象(请求其服务),对象的具体实现细节对外是不可见的。这些特征使得软件系统的体系结构是模块化的,这种体系结构易于理解、扩充和修改。当对象的接口确定以后,实现细节的修改不会影响其它对象,易于
6、维护。同时也便于分配给不同的开发人员去实现,依据规定的接口能方便地组装成系统。3. 面 向 对 象 方 法 中 的 继 承 机 制 有 力 支 持 软 件 的 复 用 。在同一应用领域的不同应用系统中,往往会涉及到许多相同或相似的实体,这些实体在不同的应用系统中存在许多相同的属性和操作,也存在一些不同的应用系统所特有的属性和操作。在开发一个新的软件系统时,可复用已有系统中的某些类,通过继承和补充形成新系统的类。在同一个应用系统中,某些类之间也存在一些公共的属性和操作,也含有它们各自私有的属性和操作。这也可以通过继承来复用公共的属性和操作。71 面向对象的基本概念Peter Coad 和 Edw
7、ard Yourdon 提出用下列等式识认面向对象方法:面向对象 = 对象(object )+ 分类(classification)+ 继承(inheritance)+ 通过消息的通信(communication with messages )可以说,采用这四个概念开发的软件系统是面向对象的。下面介绍面向对象中的几个基本概念。1对象(object)在现实世界中,每个实体都是对象,如,大学生、汽车、电视机、空调等都是现实世界中的对象。每个对象都有它的属性和操作,如电视机有颜色、音量、亮度、辉度、频道等属性,可以有切换频道、增大/ 减低音量等操作。电视机的属性值表示了电视机所处的状态,而这些属性值
8、只能通过其提供的操作来改变。电视机的各组成部分,如显像管、印制板、开关等都封装在电视机机箱中,人们不知道也不关心电视机是如何实现这些操作的。在计算机系统中,对象是指一组属性以及这组属性上的专用操作的封装体。属性通常是一些数据,有时它也可以是另一个对象。例如,书是一个对象,它的属性可以有书名、作者、出版社、出版年份、定价等属性,其中书名、出版年份、定价是数据,作者和出版社可以是对象,他们还可以有自己的属性(在有些简单的软件系统中可能只用到作者名和出版社名,而不关心作者和出版社的其他信息,那么,他们也可以是数据)。每个对象都有它自己的属性值,表示该对象的状态。对象中的属性只能通过该对象所提供的操作
9、来存取或修改。操作也称为方法或服务,它规定了对象的行为,表示对象所能提供的服务。封装是一种信息隐蔽技术,用户只能看见对象封装界面上的信息,对象的内部实现对用户是隐蔽的。封装的目的是使对象的使用者和生产者分离,使对象的定义和实现分开。一个对象通常可由对象名、属性和操作三部分组成。2类(class)类是一组具有相同属性和相同操作的对象的集合。一个类中的每个对象都是这个类的一个实例(instance)。例如 “轿车”是一个类,“轿车” 类的实例“张三的轿车”、“李四的轿车”都是对象。也就是说,对象是客观世界中的实体,而类是同一类实体的抽象描述。在分析和设计时,我们通常把注意力集中在类上,而不是具体的
10、对象上。我们也不必为每个对象逐个定义,只需对类作出定义,而对类的属性的不同赋值即可得到该类的对象实例,如图 7.所示。类和对象之间的关系类似于程序设计语言中的类型(type)和变量(variable)之间的关系。通常把一个类和这个类的所有对象称为类及对象,或称为对象类。3. 继承(inheritance)一个类可以定义为另一个更一般的类的特殊情况,如“轿车”类是“汽车”类的特殊情况,我们称一般类是特殊类的父类或超类(superclass),特殊类是一般类的子类(subclass)。如“汽车”类是“轿车”类的父类,“轿车”类是“汽车”类的子类。同样,“汽车”类还可以是“交通工具”类的子类,“交通
11、工具”类是“汽车”类的父类。这样可以形成类的一种一般特殊的层次关系,如图 7.2 所示。在这种一般特殊的关系中,子类可以继承其父类(或祖先类)的所有属性和操作,同时子类中还可以定义自己特有的属性和操作。所以子类的属性和操作是子类中的定义部分和其祖先类中的定义部分的总和。轿 车型号:字符串颜色:字符串牌照号:字符串张经理的轿车型号:桑塔纳颜色:红色牌照号:沪 AN2037类 实例图 7.1 类及实例交通工具飞行器 汽 车 船轿 车 货 车图 7.2 一般特殊关系继承是类间的一种基本关系,它是基于层次关系的不同类共享数据和操作的一种机制。父类中定义了其所有子类的公共属性和操作,在子类中除了定义自己
12、特有的属性和操作外,还可以对父类(或祖先类)中的操作重新定义其实现方法,称为“重载(override)”。例如,矩形是多边形的子类,在多边形类中定义了属性:顶点坐标序列,定义了操作:平移、旋转、显示、计算面积等。在矩形类中,可定义它自己的属性长和宽,还可以对操作“计算面积”重新定义。有时,我们定义一个类,它把一些类组织起来,提供一些公共的行为,但并不需要使用这个类的实例,而仅使用其子类的实例。我们把这种不能建立实例的类称为抽象类(abstract class), 例如图 7.中的交通工具就是一个抽象类。通常一个抽象类只定义这个类的抽象操作,抽象操作是指只定义操作接口,其实现部分由其子类定义。如
13、果一个子类只有唯一一个父类,这个继承称为单一继承。如果一个子类有一个以上的父类,这种继承称为多重继承。如图 7.所示“水陆两栖交通工具”类既可继承“陆上交通工具”类,又可以继承“水上交通工具”类的特性。4. 消息(message)消息传递是对象间通信的手段,一个对象通过向另一个对象发送消息来请求其服务。一个消息通常包括接收对象名、调用的操作名和适当的参数(如果有必要的话)。消息只告诉接收对象需要完成什么操作,但并不指示接收者怎样完成操作。消息完全由接收者解释,接收者独立决定采用什么方法完成所需的操作。5. 多态性(polymorphism)和动态绑定(dynamic binding)多态性是指
14、同一个操作作用于不同的对象上可以有不同的解释,并产生不同的执行结果。例如“画”操作,作用在“矩形”对象上,则在屏幕上画一个矩形,作用在“圆”对象上,则在屏幕上画一个圆。也就是说,相同操作的消息发送给不同的对象时,每个对象将根据自己所属类中定义的这个操作去执行,从而产生不同的结果。与多态性密切相关的一个概念就是动态绑定。动态绑定是指在程序运行时才将消息所请求的操作与实现该操作的方法进行连接。传统的程序设计语言的过程调用与目标代码的连接(即调用哪个过程)放在程序运行前(编译时)进行,称为静态绑定,而动态绑定则是把这种连接推迟到运行时才进行。在一般与特殊关系中,子类是父类的一个特例,所以父类对象可以
15、出现的地方,也允许其子类对象出现。因此在运行过程中,当一个对象发送消息请求服务时,要根据接收对象的具体情况将请求的操作与实现的方法进行连接。例如,图 7.4 表示三角形类、矩形类、六边形类都继承了多边形类,其中 polygon 类是一个抽象类,它定义了抽象操作 getArea 的接口,在 triangle 类、rectangle 类和 hexagon类中都继承了操作 getArea,即它们与父类中的 getArea 有相同的接口定义,并分别给出了水上交通工具 陆上交通工具水陆两栖交通工具图 7.3 多重继承它们各自计算面积的实现方法。在图 7.5 所示的程序中,对于不同的“条件”p 可能是 t
16、,也可能是 r,因此,当执行p.getArea 语句时,它可能计算三角形的面积,也可能计算矩形的面积。Var p : polygon ;Var t : triangle := triangle.new ;if 条件 then p := telse p := rendif ; area := p.getArea ;图 7.5 多态性实例程序7.2 面向对象分析和设计过程本节介绍面向对象分析和设计的一般过程。getArea图 7.4 多态性实例计算面积面积计算面积面积六边形计算面积面积长宽矩形计算面积面积三角形多边形7.2.1 面向对象分析(Object_Oriented Analysis,简称
17、OOA)过程1. OOA 的任务面向对象分析的目标是完成对所解问题的分析,确定待建的系统要做什么,并建立系统的模型。为达到这一目标,必须完成以下任务:1)在客户和软件工程师之间沟通基本的用户需求。2)标识类(包括定义其属性和操作)。3)刻画类的层次结构。4)表示类(对象)之间的关系。5)为对象行为建模。6)递进地重复任务 1 至任务 5,直至完成建模。其中任务 2 至任务 4 刻画了待建系统的静态结构,任务 5 刻画了系统的动态行为。2面向对象分析的一般步骤如下:1) 获取客户对系统的需求:包括标识场景(scenario)和用况(use case),以及建造需求模型。2) 用基本的需求为指南来
18、选择类和对象(包括属性和操作)。3) 定义类的结构和层次。4) 建造对象关系模型。5) 建造对象行为模型。6) 利用用况/场景来复审分析模型。3. 分析过程通常 OOA 可以从理解系统的使用方式开始,如果系统是人机交互的,则考虑被人使用的方式;如果系统涉及到过程控制,则考虑被控制对象(如,设备)使用的方式;如果系统协同并控制应用程序,则考虑应用程序的使用方式。1) 获取客户对系统的需求需求获取必须让客户与开发者充分地交流,这里介绍一种采用用况(use case)来收集客户需求的技术。分析员首先标识使用该系统的不同的执行者(actor),这些执行者代表使用该系统的不同的角色。每个执行者可以叙述他
19、如何使用系统,或者说他需要系统提供什么功能。执行者提出的每一个使用场景(或功能)都是系统的一个用况的实例,一个用况描述了系统的一种用法(或一个功能),所有执行者提出的所有用况构成系统的完整的需求。注意,执行者与用户是不同的两个概念,一个用户可以扮演几个角色(执行者),一个执行者可以是用户,也可以是其他系统(应用程序或设备)。得到的用况必须进行复审,以使需求完整。有关用况建模的细节可参见 74 节。2) 标识类和对象在确定了系统的所有用况后,即可开始标识类以及类的属性和操作。在 752 节中将详细介绍一种采用 CRC( Class-Responsibility-Collaborator)技术标识
20、类和对象的方法。3) 定义类的结构和层次在确定了系统的类后,就可定义类的结构和层次。类的结构主要有两种:一般特殊(generalization specialization)结构和整体部分(whole part)结构。一般特殊结构是一种分类结构,它反映类之间的一般与特殊的关系。例如,交通工具可分成汽车、轮船、飞机。“交通工具”类就是一个一般类,“汽车”、“轮船”、“飞机”等类就是特殊类,一般类与特殊类之间是一种“is a”的关系,如:汽车是一种交通工具。同样,特殊类还可以分为更特殊的类,如“汽车”类还可分成“轿车”、“货车”等类。这样可形成类的层次结构。整体部分结构反映了类之间的整体与部分关系
21、。例如“汽车”代表整体,而“车轮”、“发动机”、“底盘”等都是“汽车”的一部分。值得注意的是,整体-部分关系是对对象而言的,而不是对类的。整体-部分关系是一种 “has a ”的关系,如“汽车”有“发动机”。同样,整体-部分结构也具有层次结构。有的面向对象方法中,把互相协作以完成一组紧密结合在一起的责任的类的集合定义为主题(subject) 或子系统(subsystem )。主题和子系统都是一种抽象,从外界观察系统时,主题或子系统可看作黑盒,它有自己的一组责任和协作者,观察者不必关心其细节。观察一个主题或子系统的内部时,观察者可以把注意力集中在系统的某一个方面。因此,主题或子系统实际上是系统更
22、高抽象层次上的一种描述。4) 建造对象关系模型对象-关系模型描述了系统的静态结构,它指出了类间的关系(relationship)。类间的关系有多种,这将在 753 节中详细介绍。5) 建立对象行为模型对象-行为模型描述了系统的动态行为,它们指明系统如何响应外部的事件或激励(stimulus)。建模的步骤如下:(1) 评估所有的用况,以完全理解系统中交互的序列。(2) 标识驱动交互序列的事件,理解这些事件如何和特定的对象相关联。(3) 为每个用况创建事件轨迹(event trace)。(4) 为系统建造状态机图。(5) 复审对象-行为模型,以验证准确性和一致性。对象-行为建模详见 76 节。7.
23、2.2 面向对象设计(Object_Oriented Design,简称 OOD)过程面向对象设计是将 OOA 所创建的分析模型转化为设计模型。与传统的开发方法不同,OOD 和 OOA 采用相同的符号表示,OOD 和 OOA 没有明显的分界线,它们往往反复迭代地进行。在 OOA 时,主要考虑系统做什么,而不关心系统如何实现。在 OOD 时,主要解决系统如何做,因此需要在 OOA 的模型中为系统的实现补充一些新的类,或在原有类中补充一些属性和操作。OOD 时应能从类中导出对象,以及这些对象如何互相关联,还要描述对象间的关系、行为以及对象间的通信如何实现。OOD 同样应遵循抽象、信息隐蔽、功能独立
24、、模块化等设计准则。1OOD 的一般步骤面向对象设计的一般步骤如下:1)系统设计 将子系统分配到处理器 选择实现数据管理、界面支持和任务管理的设计策略 为系统设计合适的控制机制 复审并考虑权衡2)对象设计 在过程级别(procedural level)设计每个操作 定义内部类 为类属性设计内部数据结构3)消息设计使用对象间的协作和对象-关系模型,设计消息模型4)复审设计模型并在需要时迭代。2. 系统设计与系统设计有关的活动有:1) 将分析模型划分成子系统在 OO 系统设计中,我们对分析模型进行划分,将紧密结合在一起的类、关系和行为包装成设计元素,称为子系统。通常,子系统的所有元素共享某些公共的
25、性质,它们可能都涉及完成相同的功能;它们可能驻留在相同的产品硬件中;或者它们可能管理相同的类和资源。子系统由它们的责任所刻画,即,一个子系统可以通过它提供的服务来标识。在 OOD 中,这种服务是完成特定功能的一组操作。子系统的设计准则是:(1) 子系统应具有定义良好的接口,通过接口和系统的其它部分通信;(2) 除了少数的“通信类” 外,子系统中的类应只和该子系统中的其它类协作;(3) 子系统的数量不宜太多;(4) 可以在子系统内部再次划分,以降低复杂性。我们可以用类似于数据流图(DFD)的图来描述子系统之间的通信和信息流,此时,DFD 中的每个加工(process)表示一个子系统。2) 标识问
26、题本身的并发性,并为子系统分配处理器通过对对象-行为模型的分析,可发现系统的并发性。如果对象(或子系统)不是同时活动的,则它们不需并发处理,此时这些对象(或子系统)可以在同一个处理器上实现。反之,如果对象(或子系统)必须对一些事件同时异步地动作,则它们被视为并发的,此时,可以将并发的子系统分别分配到不同的处理器,或者分配在同一个处理器,而由操作系统提供并发支持。3) 任务管理设计Coad 和 Yourdon 提出如下管理并发任务对象的设计策略:(1) 确定任务的类型;(2) 必要时,定义协调者任务和关联的对象;(3) 将协调者任务和其它任务集成。通常可通过了解任务是如何被启动的来确定任务的类型
27、,如事件驱动任务,时钟驱动任务。每个任务应该定义其优先级,并识别关键任务。当有多个任务时还可以考虑增加一个协调者任务,以控制这些任务协同工作。4) 数据管理设计通常数据管理设计成层次模式,其目的是将数据的物理存储及操纵与系统的业务逻辑加以分离。数据管理的设计包括设计系统中各种数据对象的存储方式(如内存数据结构、文件、数据库),以及设计相应的服务,即为要储存的对象增加所需的属性和操作。5) 资源管理设计OO 系统可利用一系列不同的资源(如磁盘驱动器、处理器、通信线路等外部实体或数据库、对象等抽象资源),很多情况下,子系统同时竞争这些资源,因此要设计一套控制机制和安全机制,以控制对资源的访问,避免
28、对资源使用的冲突。6)人机界面设计对大多数应用系统而言,人机界面本身是一个非常重要的子系统。人机界面主要强调人如何命令系统,以及系统如何向人提交信息。它包括窗口、菜单、报告的设计。7) 子系统间的通信子系统之间可以通过建立客户/服务器连接进行通信,也可以通过端对端(peer to peer)连接进行通信。我们必须确定子系统间通信的合约( contract),合约提供了一个子系统和另一个子系统交互的方式。确定合约的设计步骤如下:(1) 列出可以被该子系统的协作者提出的每个请求,按子系统组织这些请求,并把它们定义到一个或多个适当的合约中,务必要标记那些从父类中继承的合约;(2) 对每个合约标记操作
29、(继承的和私有的),通过这些操作来实现被该合约蕴含的责任,务必将操作和子系统内的特定类相关联;(3) 每个合约应包含:合约的类型(客户机/服务器或端对端),协作者(合约伙伴的子系统名),类(子系统中支持合约蕴含服务的类名),操作(类中实现服务的操作名),消息格式(实现协作者间交互所需的消息格式);(4) 如果子系统间的交互模式比较复杂,还可以建立子系统协作图。3对象设计对象设计是为每个类的属性和操作作出详细的设计,并设计连接类与它的协作者之间的消息规约(specification of the messages)。1) 对象描述对象的设计描述可以采取以下形式之一:(1)协议描述:描述对象的接口
30、,即定义对象可以接收的消息以及当对象接收到消息后完成的相关操作;(2)实现描述:描述传送给对象的消息所蕴含的每个操作的实现细节,实现细节包括有关对象私有部分的信息,即关于描述对象属性的数据结构的内部细节和描述操作的过程细节。对对象的使用者来说,只需要协议描述就够了。2) 设计算法和数据结构为对象中的属性和操作设计数据结构和实现算法。7.2.3 设计模式(design patterns)在许多面向对象系统中,存在一些类和通信对象的重复出现的模式。这些模式求解特定的设计问题,使面向对象设计更灵活,并最终可复用。这些模式帮助设计者复用以前成功的设计,设计者可以把这些模式应用到新的设计中。一个设计模式通常可用四个信息来描述:1模式名设计模式名应具有实际的含义,它能反映模式的适用性和意图。2使模式可被应用所必须存在的环境和条件。3设计模式的特征模式特征指出一些设计的属性,调整这些属性使该模式能适应各种不同的问题。这些属性表示设计的特征,这些特征能被用于检索(通过数据库)以找到合适的模式设计的特征。4应用设计模式的结果(consequences)对于一个设计模式的使用结果表明设计决策的走向。