类图和对象图.docx
《类图和对象图.docx》由会员分享,可在线阅读,更多相关《类图和对象图.docx(25页珍藏版)》请在冰豆网上搜索。
![类图和对象图.docx](https://file1.bdocx.com/fileroot1/2022-12/11/8f235913-17bd-4f6d-b7b9-df29bb752d12/8f235913-17bd-4f6d-b7b9-df29bb752d121.gif)
类图和对象图
第5章 类图和对象图
在UML中,有两种图非常重要,一种是前面我们讲过的用例图,一种就是我们今天开始要学习的类图。
实际上我们平时的看到的大多数UML图都是类图,就像MartinFlower在《UML精粹》中说的那样,如果一个人在一条黑暗的胡同中向你走来,并且对你说:
“嘿,要不要看一个UML图?
”那么,那个图可能就是一个类图。
类图是使用最广泛的一种模型,用来表述系统中各个对象的类型以及他们之间存在的各种静态关系。
要有效的理解和掌握类图,必须首先熟悉类的概念,因此我们首先还是要从面向对象思想出发,再次探究类的内涵。
5、1类的定义
5.1.1面向对象的思想
例:
假设住在上海的张三要给住在蚌埠的朋友李四送一束鲜花,由于他们之间距离太远,不可能亲自买一个送过去。
为了解决这个问题,张三登陆到一个电子商务网站购买一束鲜花,并通过该网站将花送给李四。
而这个电子商务网站实际上就是通过我们蚌埠的某个鲜花店呢来完成这个任务。
因此在整个传递过程中,各个实体之间有如下的关联关系:
显然,现实生活中的情况要复杂的多。
电子商务网站可以接受很多人的订单,也可以与不同地方的鲜花店合作,以送给更多不同地方的人。
因此把图5-1进行抽象得到图5-2。
图5-2很好的诠释了类和对象的概念,显然张三是一个对象,可以归到“订货人”这个类中;而蚌埠的某个鲜花店显然也是一个对象,它可以归到“商户”这个类中。
因此可以说,每个对象都扮演了一个角色,并为其它成员提供特定的服务或执行特定的行为。
在我们的例子中,订货人想把完成的事(给李四送鲜花)委托给电子商务网站,而电子商务网站又委托给具体的商户(蚌埠某鲜花店),具体的商户最终通过送货人完成这个行为。
在实际过程中,张三首先在电子商务网站填写信息,然后电子商务平台向商户发送信息,商户安排送货人完成该动作。
也就是说:
在面向对象世界中,行为的启动是通过将“消息”传递给对此行为负责的对象来完成的;同时还将伴随着执行要求附上相关的信息(参数);而收到该消息的对象则会执行相应的“方法”来实现需求
这就是在面向对象中,用类和对象表示现实世界,用消息和方法来模拟现实世界的核心思想。
5.1.2类的定义
在深入了解类的意图后,我们应该了解一下类的组成。
我们知道类是对一组具有相同属性、操作、关系和语义的对象的描述。
这些对象包括了现实世界中的物理实体、商业实体、逻辑事物、应用事物等,甚至也包括了纯粹概念性的事物,它们都是类的实例。
关系是类之间的,语义是蕴藏的。
对于一个类而言,它的关键特性是属性(成员变量)和操作(成员方法)。
类的UML表示是一个矩形,垂直地分为三个区,如图1显示一个图形如何作为UML类建模。
顶部区域显示类的名字。
中间的区域列出类的属性。
底部的区域列出类的操作。
图1正如我们所能见到的,这个类的名字是Shape,在中间区域可以看到Shape类的4个属性:
origin、size、fillcolor、count,其中count有下划线,表示该属性是静态属性。
在底部区域中Shape类有shape()、move()、resize()和display()4个操作(或者说方法)。
方法shape()的版型为《constructor》,表示该方法是构造方法,而shape类的版型为《graphics》.
当在一个类图上画一个类元素时,必须要有顶端的区域,下面的二个区域是可以选择的,或者说是可以隐藏的(当图描述仅仅用于显示分类器间关系的高层细节时,下面的两个区域是不必要的)。
1、类名
类的名称是每个类中所必须有的构成元素,用于同其他的类相区别。
类的命名应该来自系统的问题域,并且应该尽可能的明确、无歧异。
因此,类的名字应该是一个名词,并且不应该有前缀或后缀。
类名是一个文本串,表示方法有两种:
(1)简单名simplename:
图1中的shape,它就只有一个单独的名称。
(2)全名:
也称路径名pathname,就是在类名前面加上包的名字。
如:
Banking:
:
CheckingAccount,Banking是包名,CheckingAccount是包Banking中的一个类。
对于类的命名规范,UML中并没有明确定义,只要是由字符、数字、下画线组成的唯一的字符串即可。
但在实际应用中,有一个普遍采用的命名原则:
采用CamelCase格式(大写字母开头,混合大小写,每个单词一大写开始,避免使用特殊符号),尽可能避免使用缩写。
2、类的属性
类的属性节(中部区域)列出了类的每一个属性。
在UML中,类的属性的语法格式为:
[可见性]属性名[:
类型][‘[‘多重性[次序]‘]‘][=初始值][{特性}]
注意:
[]内的部分是可选的。
根据需要,每个属性可以包括属性的可见性、属性名、类型、多重性、初始值和特性。
(1)可见性。
属性具有不同的可见性。
可见性描述了该属性(或操作)对于其他类是否可见,以及是否可以被其他类引用,而不仅仅是被该属性所在的类可见。
类的属性的可见性主要包括公有public、私有private和受保护Protected3种。
如果类的某个可见性是公有可见性,那么可以在这个类的外部使用和查看该属性;如果属性是私有可见性,那么就不能从其他的类中访问这个属性;受保护的可见性,常常与泛化和特化一起使用。
在UML中定义了4种可见性,其他种类的可见性可以由编程语言自己定义。
UML中不存在默认的可见性,如果没有显示任何一种符号,就表示没有定义该属性的可见性。
(2)属性名。
每个属性必须有一个名字和类中的其他属性相区别。
通常,属性名由描述所属类的特性的名词或名词短语组成。
按照UML约定,单字属性名要小写,如例子中的origin、size等。
如果属性名包含了多个单词,这些单词要合并,并且除第一个单词外其余单词的首字母要大写,如例子中的fillColor.
(3)类型。
属性具有类型,用来说明该属性是什么数据类型。
象整型、布尔型、实型和枚举等这些简单类型在不同的编程语言中有不同的定义。
但在UML中,类的属性可以使用任意类型,包括系统中的其他类。
如在业务类图中,属性类型通常与单位相符,这对于图的可能读者是有意义的(例如,分钟,美元,等等)。
然而,用于生成代码的类图,要求类的属性类型必须限制在由程序语言提供的类型之中,或包含于在系统中实现的、模型的类型之中。
(4)初始值。
设定初始值有两个好处:
保护系统的完整性,防止漏掉取值或被非法的值破坏系统的完整性;为用户提供易用性。
如,在银行账户应用程序中,一个新的银行账户会以零为初始值。
显示属性默认值是可选择的;图2显示一个银行账户类具有一个名为balance的类型,它的默认值为0。
(5)多重性:
表示该属性可能的取值。
(6)特性:
是用户对该属性性质的一个约束说明,例如{onlyread}说明该属性的值不能被修改。
例:
属性声明
+size:
Area=(100,100)
#visibility:
Boolean=false
name:
String[0..1]
point:
Point[2..*ordered]
3、类的操作
操作是类所提供的服务,用于修改、检索类的属性或执行某些动作。
操作通常也叫做函数或方法,它们位于类的内部,并且只能作用于该类的对象上。
操作的语法格式为:
[可见性]操作名[(参数列表)][:
返回类型][{特性}]注意:
[]内的部分是可选的。
(1)可见性,与属性的可见性类似。
(2)操作名:
用来描述所属类的行为的动词或动词短语,命名规则和属性名的情况类似。
5.2类之间的关系
类之间最常用的关系有4种,即关联、依赖、泛化和实现关系。
5.2.1关联
关联是模型元素间的一种语义联系。
如:
一个人为一家公司工作,一家公司有许多办公室。
我们就认为人和公司、公司和办公室之间存在某种语义上的联系。
注意:
链和关联。
就像对象是类的实例,链是关联的实例,类之间的关联表示类与类之间的关系,而链表示对象和对象之间的关系。
在UML类图中,关联关系用一条连接两个类的实线表示。
如图:
关联的任何一个连接点都叫做关联端,一个关联可以有两个或多个关联端,每个关联端连接到一个类。
除了以上关联的基本形式外,还有几种应用于关联的修饰,如分别是关联名、角色、多重性、聚合、组合和导航性等等。
1、关联名
关联可以有名称,用来描述关联的性质和作用。
如图所示是一个使用关联名的例子,其中类Company和Person之间的关联如果不使用关联名,可以有多种解释。
如Person类可以表示公司的客户、雇员或所有者等等。
但如果在关联上加了Employs这个关联名,则表示Company类和Person类之间是雇佣关系。
这样可以使得类之间的关联语义更加清晰明确。
通常,关联名是一个动词或动词短语。
此外,为了消除名称含义的歧义,还可以提供一个指引读名称方向的实心三角形,给名称一个方向。
在类图中,并不需要给每个关联都加上关联名。
只有在需要明确的给关联提供角色名,或一个模型存在多个关联且要查阅、区别这些关联时才给出关联名。
2、关联的角色
关联两端的类可以以某中角色参与关联。
角色是关联关系中一个类对另一个类所表现出来的职责。
当类出现在关联的一端时,该类就在关联关系中扮演了一个特定的角色。
角色的名称是名词或名词短语。
如图,类Company以employer的角色,Person类以employee的角色参与关联。
Employer和employee称为角色名。
如果在关联上没有标出角色名,则隐含的用类名作为角色名。
相同类可以在其他的关联中扮演相同或不同的角色。
3、多重性multiplicity
约束是UML的三大扩展机制之一,多重性是其中的一种约束,也是使用最广泛的一种约束。
多重性又叫重数,用来说明关联的两个类之间的数量关系;或者说关联的多重性是指有多少对象可以参与该关联,它可以用来表达一个取值范围、特定值、无限定的范围或一组离散值。
多重性的格式为:
n..m,其中整数n定义所连接的最少对象的数目,m则是最多对象数(当不知道确切的最大数时,UML中用*表示最大数,Rose中则用n来表示。
常见多重性定义
表示
含义
0..1
0个或1个
1(1..1)
只能1个
0..*(0..n)
0个或多个
*(0..n)
0个或多个
1..*(1..n)
1个或多个
3
只能3个
0..5
0到5个
5..15
5到15个
雇主(公司)可以雇佣多个雇员,表示为0..n;而雇员只能被一家公司雇佣,表示为1.
多重性是用非负整数的一个子集来表示。
4、导航性
关联也可以有方向,即导航性,在关联关系上加上导航箭头表明可以从源类的任何对象到目标类的一个或多个对象。
箭头指向的是目标类,另外一边则是源类。
只在一个方向上有箭头的关联称为单向关联;在两个方向上都可以导航的是双向关联,用一条没有箭头的实线表示,等价于两端都有箭头。
5、关联类
在两个类之间的关联中,关联本身也可以有特性。
例如,在company和person之间的雇主/和雇员关系中,有一个描述该关联特性的contract.,它只应用于一对company和person。
Contract类中的属性Salary和dateHired,描述的是company类和person类之间的关联关系,而非描述的company类或和person类的属性。
在UML中,把这种情况建模为关联类,关联类可以进一步描述关联关系的属性、操作以及其他信息。
关联类是一种具有关联特性和类特性的建模元素,可以把它看成是具有类特征的关联或是具有关联特征的类。
关联类通过一条虚线与相应得关联连接。
生成Java代码。
6、关联的约束
图中,两个关联之间有一个虚线,上面写着{xor}。
在UML中,这种以大括号括起来的,放在建模元素外面的字符串就是约束。
约束可以是自由文本和OCL两种形式表示。
本例中是自由文本。
在关联上加上约束,可以加强关联的含义。
例子显示的是两个关联之间存在异或约束,表示Account类要么与Person类有关联,要么与company类有关联,但不能同时与company类和person类都有关联。
约束不仅可以作用在关联上,也可以应用于其他建模元素上。
7、限定关联与限定符
如图,在Bank和Person的关联关系中,Bank端多了一个方框,里面写着account,它在UML中称为限定符(qualifier),一般限定符在关联端紧靠源类图标。
存在限定符的关联称为限定关联(qualifiedassociation)或受限关联,用来表示某种限定关系。
限定符的作用就是在给定关联一端的个对象和限定符值以后,可以确定另一端的一个对象或对象集。
在本例中,它的意思是一个person可以在Bank中有多个account。
但给定一个account值后,就可以对应一个Person值,或者对应的person值为null,因为person端的多重性为0..1。
这里的多重性表示的是(bank,acount)之间的关系,而非person和Bank之间的关系。
即:
(bank,acount)→0个或1个person
Person→多个(bank,acount)
在左图中没有说明Person类和Bank类之间是1对多的关系还是1对1的关系,一个person可能只对应一个Bank,也有可能对应多个Bank。
如果要明确它们之间的对应关系,应该在person类和Bank类之间增加关联描述(图:
限定关联和一般关联)。
应当注意的是:
限定符是关联关系的属性,而不是类的属性。
那么在实现图:
限定符和限定关联中的结构的时候,account这个属性可能是Person类中的一个属性,也可能是Bank类中的一个属性,甚至是其他类中的属性。
如果一个应用系统需要根据关键字对一个数据集进行查找操作,经常会用到限定关联。
引入限定符的一个目的就是把多重性从n降为1或0..1,这样如果做查询操作,那么返回的对象最多是一个,而不是一个对象集。
如果查询操作的结果是单个对象,则这个查询操作的效率就比较高。
所以在使用限定符的时候,如果限定符另一端的多重性仍为n,那么引入这个限定符的意义就不大了。
限定符举例2:
考虑一个制造厂的工作台建模问题。
在工作台上对返回的工件进行修理。
如图,要对类WorkDesk和ReturnItem之间的关联建模。
在WorkDesk的语境中有一个标识具体的ReturnItem的JobID。
在此,JobID是关联的属性,不是ReturnItem的特征,因为工件没有诸如修理或加工这样的信息。
给定一个WorkDesk对象并给定JobID一个值,就可以找到0个或1个ReturnItem的对象。
8、关联的种类
一个关联可以有两个或多个关联端,每个关联端连接到一个类。
而根据关联所连接的类的数量,类之间的关联可以分为3种关联,即自返关联、二元关联和N元关联(多元关联)。
(1)自返关联(reflexiveassociation)又称递归关联,是一个类与它本身相关联,也就是同一个类的两个对象间的关联。
当一个类关联到它本身时,这并不意味着类的实例与它本身相关,而是类的一个实例与类的另一个实例相关,或者说自返关联虽然只有一个关联类,但有两个关联端,每个关联端的角色不同。
图一个自返关联的实例
图14显示一个Employee类如何通过manager/manages角色与它本身相关。
该图描绘的关系说明一个Employee实例可能是另外一个Employee实例的经理。
然而,因为“manages”的关系角色有0..*的多重性描述;一个雇员可能不受任何其他雇员管理。
(2)二元关联:
指在两个类之间进行关联。
(3)N元关联:
是在3个或3个以上类之间的关联。
N元关联中多重性的意义是:
在其他N-1个实例值确定的情况下,关联实例元组的个数。
N元关联的例子如图所示,Player、Team和Year之间存在三元关联,record类是关联类。
在次,多重性表示在某个具体的年份year和运动队team中,可以有多少个运动员player;一个运动员在某个年份中,可以在多少个运动队服役;以及同一个运动员在同一个运动队中可以服役多年。
例:
在NBA中,球员可以转会或被交换到另外的球队中,假设,2008年6月火箭队的麦克格雷迪转会到达拉斯小牛队,那么2008年麦克格雷迪就在两个NBA球队中服役过;姚明从2003年起在火箭队服役,到2008年就有5年了。
N元关联中没有限定符,也没有聚集、组合的概念。
在UML中,N元关联用菱形表示,但Rose2003中不能直接表示。
5.2.2特殊关联――聚合(聚集)和组合
1、聚合(聚集)
(1)聚集是一种特殊形式的关联,它表示的是部分与整体(partof)的关系。
可以分成两个方面:
(1)一个对象是另一个对象的一部分的时候,二者之间的关系
(2)反过来说就是,一个对象由一组其他对象聚集而成时的关系。
聚合关系的含义是“聚”在一起的意义,也就是表示“部分”可以独立于“整体”而存在。
在UML中,使用一个带空心菱形的实线表示聚集,空心菱形指向的是代表“整体”的类。
例1:
Circle类和Style类之间是聚集关系。
一个圆可以有颜色、是否填充这些式样方面的属性,可以用一个Style对象表示这些属性,不过同一个Style对象也可以表示别的对象如三角形的一些样式方面的属性,即Style对象可以用于不同的地方。
如果circle这个对象不存在了,并不意味着Style这个对象也消失了。
例2:
电子邮件消息(MailMessage)一般包括一个标题(Header)、一个正文体(Body)以及若干个附件(Attachment)等几部分。
如图所示,多重性也可以用于聚合关系,与普通关联的使用方式相同。
除了表示一个消息中标题、正文体和附件的不同数目外,示例2中指定的多重性还表明,不同于标题和正文体,一个附件可以同时作为多个消息的一部分。
(2)聚合的性质(*不讲)
●反对称性:
是指作为聚合的实例的连接不能将对象连接到自己(《面向对象设计UML实践》P126),即聚合关系得实例之间不能形成环。
●传递性:
如果A是B的一部分,B是C的一部分,那么A也是C的一部分。
更形式的说,如果对象A链接到B,B链接到C,并且其链接是同一个聚合的实例,那么应该认为A也链接到C。
上述性质只适用于聚合使对象能够链接到自己的类的实例的情况中。
2、组合
组合也表示整体与部分的关系,它是聚集关系的一种特殊情况,是更强形式的聚合,又被称为强聚合。
在组合中,部分与整体具有相同的生命周期,“部分”对象完全依赖于“整体“对象。
这种依赖性表现在两个方面:
一是,部分对象一次只能属于一个组合对爱能够,第二,当组合对象销毁的时候,它的所有从属部分都必须同时销毁。
在UML中,组合关心用带有实心菱形的实线表示,实心菱形指向的是“整体”。
例:
(1)Circle类和point类之间是组合关系。
一个圆可以由半径和圆心确定,如果圆不存在了,那么表示这个圆的圆心也就不存在了,所以Circle类和point类之间是组合关系。
(2)当在窗口系统中创建一个Frame时,必须把Frame附加到一个它所归属的Window中。
类似的,当撤销一个Window时,Window对象必须依次撤销它的Frame部分。
(3)在电子邮件消息的示例中,将消息和它的标题及正文体之间的关系作为组合关系建模可能是合理的,因为有可能一旦消息被删除,那么标题和正文体也就不存在了,而他们存在的时候是唯一的一个消息。
但是,消息及其附件之间的关系不太可能用组合恰当的建模。
首先,在同一时间,附件可以属于多个消息,其次,附件有可能被保存。
因此它们的生命周期将超过其所附属的消息的生命周期。
3、聚合和组合的区别
(1)聚合和组合是比较容易混淆的概念,实际运用中很难确定是用聚合关系还是组合关系。
因此在设计时,采用聚合还是组合,要根据应用场景来判断部分类和整体类之间的关系。
如:
电脑是一个整体类,而主板、Cpu等则是相对于它的部分类。
那么它们是聚合还是组合呢?
如果在一个固定资产管理系统中,可能适合的就是“组合”;而对于在线DIY系统,那么显然应该采用“聚合”关系。
对于组合而言,比较容易理解的就是订单和订单项之间的关系,如果订单不存在,显然订单项就没有了意义,因此必然是组合关系。
所以判断是聚合还是组合关系,关键在于要放到具体的应用场景中讨论。
其实不管是聚合还是组合,对于面向对象的编程实现而言,并没有实质的区别,因此对这个问题不需要紧抓不放,它只是起到一个模型更清晰化的作用,在实践中不必过于执着。
(2)概念上的区别:
●聚合关系也是has-a关系,组合关系也叫contains-a关系;
●聚合关系表示事物的整体\部分关系较弱的情况,组合关系表示事物的整体\部分关系较强的情况
●在聚合中,代表部分事物的对象可以属于多个聚合对象,可以为多个聚合对象所共享,并且可以随时改变它所从属的聚合对象,代表部分事物的对象与代表聚合事物对象的德生存周期无关,一旦删除了它的一个聚合对象,不一定也就随即删除代表部分事物的对象。
在组合关系中,代表整体事物的对象负责创建和删除代表部分事物的对象,代表部分事物的对象只属于一个组合对象,一旦删除了组合对象,也就随即删除了相应得代表部分事物的对象。
5.2.3泛化
泛化定义了一般元素和特殊元素之间的分类关系。
例:
考虑一个银行,向顾客提供各种账户,包括活期账户帐户、存款(定期)账户以及在线账户。
该银行操作的一个重要方面是一个顾客实际上可以拥有多个账户,这些账户属于不同的类型。
建模类图:
存在问题:
(1)模型中有过多关联。
从顾客角度,拥有一个账户只是一种简单的关系,并不会受到可以拥有不同种类账户这个事实的很大影响。
但是,在这个图中将对此用3个不同的关联建模,因此会破坏模型概念上的简化。
更糟糕的是,如果增加一个新类型的账户,就不得不向模型中增加一个新的关联,以允许顾客持有新类型的账户。
(2)不同种类的账户被作为完全不相关的类建模。
然而,他们可能会有大量的公共结构,可能定义了许多类似的属性和操作,如果建模表示法可以提供一种方式明确表示这种公共结构,那是比较理想的。
通过泛化可以克服这些问题。
我们可以定义一个Account类,对各种账户共有的东西舰模,然后将代表特定种类账户的类表示为这个一般类的特化。
泛化关系用一条从特殊元素指向一般元素的空心三角箭头表示。
从面向对象程序设计的角度来说,类与类之间的泛化就是类与类之间的继承关系。
泛化描述的是“akindof”关系。
如彩色电视机、黑白电视机都是电视机的一种,汽车是一种交通工具。
在类中,一般元素称为父类或超类,特殊元素为子类。
在UML中,泛化可以通过可替换性的概念阐述。
它意味着在任何需要一个父类的实例的地方,都可以用一个子类的实例代替。
利用替换性,可以简化上述银行模型,使用一个关联替代图中的3个关联。
直观上,这个新图规定顾客可以拥有任何数目的账户,并且这些账户可以是如子类所定义的各种不同类型的账户。
这个替换如下:
在上图中的关关联隐含着Customer类和Account类在运行时可以链接,因为可替换性,这些链接中的任何Account实例都可以用Account的任一子类的实例代替。
替换过程可能产生如下的链接,图中顾客链接到他们实际拥有的各种账户。
拥有各种账户
在此顾客和活期账户对象之间的链接是图:
"拥有账户"的单一关系中的顾客和账户类之间关连的一个实例。
这种现象是多态性的一种形式,在这里,多态指账户类的各种子类