前几周画活动图,用例图,这周画类图
类图在UML的静态机制中是重要的组成部分,它不但是设计人员关心的核心,更是实现人员关注的重点。建模工具也主要根据类图来产生代码。类图在UML的9种图中占据了相当重要的地位。
类图是用来显示系统中的类、接口及它们之间静态结构和关系的一种静态模型,它用于描述系统的结构。类图的建模贯穿系统的分析和设计阶段的始终,通常从用户能够理解的用例开始建模,最终到系统开发小组能够完全理解的类。本章将重点介绍类图和对象图及其相关的概念。
本章要点
重点理解类图和对象图的相关概念,掌握类的定义及类的可视化表示。理解并掌握类之间的关系:依赖关系、泛化关系、关联关系和实现关系。理解对象的基本概念及对象的可视化表示。能够利用类图正确地描述系统结构。
5.1 类图和对象图概述
5.1.1 类图的概述
类是对一组具有相同属性、操作、关系和语义的对象的抽象。主要包括名称部分(Name)、属性部分(Attribute)和操作部分(Operation)。在UML中类用一个矩形框表示,它包含3个区域,最上面是类名,中间是类的属性,最下面是类的方法,如图5.1所示。
1.名称
每个类都必须有一个能和其他类进行区分的名称,类的名称部分是不能省略的,其他组成部分可以省略。名称(Name)是一个文本串,类的命名要求由字符、数字、下划线组成的唯一的字符串即可。表示方法有以下两种。
(1)简单名:如图5.2中的Account,它只是一个单独的名称。
(2)全名:也称为路径名,就是在类名前面加上包的名称,如Business:Account。
2.属性
属性描述了类在软件系统中代表的事物(即对象)所具备的特性。类可以有任意数目的属性,也可以没有属性。类如果有属性,则每一个属性都必须有一个名字(如图5.2中的Account类中的balance属性),另外还可以有其他的描述信息,如可见性、数据类型、缺省值等,如图5.2所示。
在UML中,类属性的语法为:
[可见性]属性名[:类型][=初始值][{属性字符串}]
(1)可见性:类中属性的可见性主要包括公有(Public)、私有(Private)和受保护(Protected)。在UML中,用“+”表达公有类型,用“-”表达私有类型,而用“#”表达受保护类型。UML的类中不存在默认的可见性,如果没有显示任何一种符号,就表示没有定义该属性的可见性。
(2)属性名:每个属性都必须有一个名字以区别于类中的其他属性,是类的一个特性。属性名由描述所属类的特性的名词或名词短语组成。按照UML的约定,单字属性名小写。如果属性名包含多个单词,这些单词要合并,且除了第一个单词外其余单词的首字母要大写。
例如,在图5.2中balance是属性名,是私有属性。
(3)类型:说明属性的数据类型。在类的图标里,可以指定每个属性值的类型。可能的类型包括字符串(string)、浮点型(float)、整型(int)和布尔型(boolean),以及其他的枚举类型。指明类型时,需要在属性值后面加上类型名,中间用冒号隔开。还可以为属性指定一个默认值。
(4)初始值:为了保护系统的完整性,防止漏掉取值或被非法的值破坏系统的完整性,可以设定属性的初始值。图5.2中的balance属性的数据类型是double,且初始值等于“1”。
(5)属性字符串:属性字符串用来指定关于属性的其他信息,例如,某个属性应该是永久的。任何希望添加在属性定义字符串值但又没有合适地方可以加入的规则,都可以放在属性字符串里。
3.操作
操作是对类的对象所能做的事务的一个抽象。一个类可以有任意数量的操作或者根本没有操作。类如果有操作,则每一个操作也都有一个名字,其他可选的信息包括可见性、参数的名字、参数类型、参数默认值和操作的返回值的类型等。
在UML中,类操作的语法为:
[可见性]操作名[(参数表)][:返回类型][{属性字符串}]
(1)可见性:类中操作的可见性主要包括公有(Public)、私有(Private)、受保护(Protected)和包内公有(Package)。在UML中,公有类型用“+”表示,私有类型用“-”表示,受保护类型则用“#”表示,而包内公有类型用“~”表示。
(2)操作名:用来描述所属类的行为的动词或动词短语。
(3)参数表:一些按顺序排列的属性定义了操作的输入。它是可选的,即操作不一定必须有参数。
参数的定义方式为“名称:类型”。若存在多个参数,将各个参数用逗号隔开。参数可以具有默认值。
(4)返回类型:是可选的,即操作不一定必须有返回类型。绝大部分编程语言只支持一个返回值。在编程语言中一般要加一个关键字void来表示无返回值。
(5)属性字符串:在操作的定义中加入一些除了预订义元素之外的信息。
像前面给类的属性指定附加信息一样,也可以给操作指定附加信息。在操作名后面的括号中可以说明操作所需要的参数和参数的类型。有一种操作叫函数(function),它在完成操作后要返回一个值,可以指明函数的返回值及返回值的类型。
例如,图5.2中共有两个操作,分别是Deposite(Amount:double):int和ComputeInterest()。其中Amount:double是参数列表,int是操作返回值的类型。
4.职责
可以在操作列表框下面的区域说明类的职责,职责用来说明类要做什么或说明另一个类的信息。类的职责可以是一个短语或一个句子。在UML中,把职责列在类图底部的分隔栏中。在图5.3中,借阅者类的职责是借阅者可以从图书管理系统中借阅图书和将图书归还。
5.约束
说明类的职责是消除二义性的一种非形式化的方法,形式化的方法是使用约束。约束指定了该类所要满足的一个或多个规则。在UML中,约束用{}的格式写在类的边上,指定个别属性的取值范围。
括号中的文本指定了该类所要满足的一个或者多个规则。例如,假设你想指定借阅者类的类别只能是教师、学生或者行政管理人员,也就是给借阅者类的“类别”属性加上约束,可以在借阅者类图标的旁边写一个约束“{类别=教师or学生or行政管理人员}”,如图5.3所示。
【例5-1】在图书管理系统中的借阅者类中,类名为借阅者,该类共有5个属性,即借阅证号、是否有借阅资源、姓名、性别和类别,操作有借书和还书。如图5.3所示。
在图5.3中,借阅者是类的名称,在5个属性中,借阅证号、是否有借阅资源是私有属性,类型分别为int和boolean。姓名、性别和类别属性是公有属性,类型都是string。两个操作都是公有的,均没有返回值。
图5.3 借阅者类
图5.2中的Account类为例,这个类可以用C++实现的程序如下:
5.1.2 对象图的概述
类图是描述类、接口、协作以及它们之间关系的图,用来显示系统中各个类的静态结构。类图是定义其他图的基础,在类图基础上,可以使用状态图、协作图、组件图和部署图等进一步描述系统其他方面的特性。前面详细地介绍了类的基本概念,下面将对对象进行具体的介绍并将类和对象进行比较。
1.什么是对象
对象指的是一个单独的、可确认的物体、单元或实体,它可以是具体的也可以是抽象的,在问题领域里有确切定义的角色。换句话说,对象是边界非常清楚的任何事物。一个对象通常包含以下几部分。
标识(名称):为了将一个对象与其他的对象区分开,通常会为对象确定一个“标识”,也就是“对象名”。
状态(属性):对象的状态包括对象的所有属性(通常是静态的)和这些属性的当前值(通常是动态的)。
行为(方法,事件):没有一个对象是孤立存在的,对象可以被操作,也可以操作别的对象。而行为就是一个对象根据它的状态改变和消息传送所采取的行动和所做出的反应。
人们经常会将对象和类的概念混淆,对象和类的区别如下。
对象是一个存在于时间和空间中的具体实体,而类仅代表一个抽象,抽象出对象的“本质”。
类是共享一个公用结构和一个公共行为对象集合。
类是静态的,对象是动态的;类是一般化,对象是个性化;类是定义,对象是实例;类是抽象,对象是具体。
2.对象图
对象图(Object Diagram)描述的是参与交互的各个对象在交互过程中某一时刻的状态。对象图可以被看做是类图在某一时刻的实例。在UML中,对象图使用的是与类图相同的符号和关系,因为对象就是类的实例,如图5.4所示。
对象图主要包括以下几部分。
对象名:由于对象是一个类的实例,因此其名称的格式是“对象名:类名”,这两个部分是可选的,但如果是包含了类名,则必须加上“”,另外,为了和类名区分,还必须加上下划线。
属性:由于对象是一个具体的事物,因此所有的属性值都已经确定,通常会在属性的后面列出其值。
图5.4 对象图
对象图和类图在UML图形表中是很相似,但是两者也存在区别,如表5.1所示。
5.1.3 接口
接口(Interface):是描述类的部分行为的一组操作,它也是一个类提供给另一个类的一组操作。通常接口被描述为抽象操作,也就是只用标识(返回值、操作名称、参数表)说明它的行为,而真正实现部分放在使用该接口的对象中,也就是说,接口只负责定义操作而不负责具体的实现。
接口的模型表示法和类大致相同,都是用一个矩形图标来表示。其不同之处在于,接口只是一组操作,没有属性。在UML图形中,接口的表示和类图的表示类似,只是在最上面的一层类名前加描述<<interface>>,或是用一个圆圈表示。如图5.5所示。
图5.5 接口
5.1.4 抽象类
抽象类是包含一种或多种抽象方法的类,它本身不需要构造实例。定义抽象类后,其他类可以对它进行扩充,并且通过实现其中的抽象方法使抽象类具体化。在UML中,抽象类的图形表示和类图一样,只是在最上面一层的类名前加描述<<abstract>>或是在类的属性描述上设置该类为抽象类,抽象类的类名用斜体表示。如图5.2所示的Account类被定为抽象类,其类名使用斜体表示。
接口与抽象类非常相似,例如两者都不能产生实例对象,都可以作为一种定义使用。但接口和抽象类仍有本质的不同,这些不同包括:
(1)抽象类可以包含某些实现代码,但接口没有任何实现部分;
(2)抽象类可以包含属性,而接口没有属性;
(3)接口可以被结构继承,但抽象类不可以;
(4)抽象类可以有构造函数和析构函数,而接口没有;
(5)抽象类可以继承其他类和接口,而接口仅仅能继承接口;
(6)接口支持多继承,而抽象类仅仅支持单继承。