3.4 类图
在面向对象的方法中,系统中的任何事物都被看成是对象,通过对象间的交互实现系统的功能,而类是创建对象的模板,找出系统中的类是系统运行的重要前提。在面向对象建模中,类图(Class Diagram)描述了系统中的类、接口、协作及它们之间的静态关系。类图不仅定义系统中的类,表示类之间的联系,还包括类的内部结构。类图描述的是一种静态关系,在系统的整个生命周期都是有效的。图3-7所示的是某库存管理系统的部分类图。
图3-7 库存管理系统部分实体类图
在UML中,类图定义了系统中的类、描述了类的内部结构、表示了类之间的联系。
3.4.1 类
类是一组具有相同属性、操作、关系和语义的对象的集合,类用3栏矩形表示,各层从上至下依次表示类名、属性和操作,如图3-7所示,其中,类名是必须填写且是唯一的,而属性和操作是可选的。面向对象方法中,用例的执行是通过相关对象及其之间的交互来实现的,因此可以通过分析用例的实现过程寻找类:
·用例描述中出现了那些实体?
·用例的完成需要哪些实体合作?
·用例执行过程中会产生并存储哪些信息?
·用例要求与之关联的每个角色的输入是什么?
·用例反馈与之关联的每个角色的输出是什么?
·用例需要操作哪些硬设备?
根据在用例实现中所起的作用,将系统中的类分为以下3种类型:
1)边界类(Boundary Class):位于系统与外界的交界处,包括窗体、对话框、报表、直接与外部设备交互的类、直接与外部系统交互的类等。边界类主要负责接收来自执行者的信息,通常,每个执行者和用例之间至少要有一个边界类。
2)实体类(Entity Class):保存的是要放入永久存储体的信息,即每个实体类在数据库中有相应的表。正常情况下,我们把每个实体类映射为一个表,每个属性映射为一个列。实体类中定义了为实现用例所需的具体方法。
3)控制类(Eontrol Class):负责协调边界类和实体类的工作,它负责接收边界类的信息,并将其分发给实体类。一般一个用例对应一个控制类对象,控制用例中的事件顺序,控制类也可以在多个用例间共用。
图3-8是“查看商品”用例的类图,其中“查询界面”是边界类,负责接收顾客输入的商品查询条件;“商品”是实体类,负责根据查询条件调用自身定义的相关方法从商品表中查询出商品信息;“查找商品”是控制类,负责转递“查询界面”传来的商品查询条件给“商品”,并将“商品”查到的商品信息返回给“查询界面”。
图3-8 “查看商品”用例分析类图
3.4.2 类的属性
属性描述了类的所有对象共有的特征,一个类可以有一个或多个属性。在UML中,描述类属性的完整语法格式为
[可视性]属性名[:类型][=初始值][约束特性]其中[]部分的内容是可选的。
可视性表明属性是否可以被其他类使用,常见的可视性分为3种:
1)公用的(Public):任何外部类都可以使用该属性,用“+”表示。
2)私有的(Private):只有所属类本身可以使用该属性,用“-”表示。
3)受保护的(Protected):所属类及其子孙类可以使用该属性,用“#”表示。
图3-9 类的属性
类型表示属性的数据类型,可以是基本数据类型,如整型、实数、布尔型、字符串型,也可以是用户自定义的类型。
初始值是新建该类对象时属性的默认取值,可以没有。
约束特性表示用户对该属性性质的约束说明,如“ReadOnly”说明该属性是只读的。
图3-9中的Warehouse类定义了4个属性,其中houseID是字符串型的公用属性,city是字符串型的受保护属性,area是整型的受保护属性,available是字符型的私有属性,初始值为yes。
任何一个类的属性都可以有几十甚至几百个,但在一个系统中具体使用哪些属性则要根据系统的性质来确定,例如,对于教师类,在教务选课系统中需要确定教师职工号、姓名、简历、讲授课程等教学相关属性,而财务系统中则关心教师职工号、姓名、职称、工资、保险金等财务相关属性。
3.4.3 类的操作
操作是类的所有对象共有的行为,一个类可以有任何数量的操作,它们只可以被作用到该类的对象上。在UML中,描述类操作的完整语法格式为
[可视性]操作名([参数列表])[:返回类型][特性串]
其中[ ]部分的内容是可选的。
可视性表明操作是否可以被其他类使用,其类型、意义及表示方法与属性相同。
参数列表是操作在执行过程中需要的一个或多个数据,采用“参数名 类型”的方式定义,如果有多个参数,则用逗号分开。
返回类型是操作执行完毕后向外界返回的数据值的类型,其取值范围同属性的类型。
特性串说明了该操作的一些有关信息,是一个可选项。
图3-10中的Warehouse类定义了5个公用操作,其中getHouseID()操作返回的是字符串型数据,其他4个操作返回的都是空值。
图3-10 类的操作
在面向对象方法中,一个类可能含有以下4种不同类型的操作。
1)访问设置属性的操作。类的属性通常是私有或受保护的,其他类必须通过访问该类的操作来访问其属性,通常以“get+属性名”表示获取属性值操作,以“set+属性名”表示设置属性值操作。
2)创建和删除对象的操作。访问类的属性和操作前必须先将类实例化,即创建该类的对象,当不再使用时,可删除相应对象。
3)实现功能的操作。根据用户需求从功能实现过程中获取的方法。
4)辅助一个类完成自身任务的操作。通常是用于完善类自身的操作,是类私有的。
3.4.4 类之间的关系
类之间的关系主要包括5种:关联关系、聚集关系、依赖关系、泛化关系和实现关系。
1.关联关系(Association)
关联关系是指类之间存在的某种语义上的固定关系,用一条实线表示。如图3-11所示的班级和班主任之间存在管理的关联关系,图3-8所示的查询界面和查找商品之间存在消息传递的关联关系。
除了建立关系,很多时候还需考察其中的数量对应关系,根据参与关联的双方对象的数量,可以分为以下3种关联关系。
1)一对一关联:例如一个班级只有一个班主任,一个班主任只负责一个班级,则班级和班主任之间是一对一的关联关系。
2)一对多关联:例如一个班级包括多个学生,一个学生只属于一个班级,则班级和学生之间是一对多的关联关系。
3)多对多关联:例如一个学生可以选修多门课程,而一门选修课也可以被多个学生选修,则学生和选修课之间是多对多的关联关系。
关联关系用带重数符号的实线表示,重数是指明类之间数量关系的符号,可用以下几种方式表示。
1)“1..1”表示1个,是重数的默认值。
2)“0..1”表示0个或1个。
3)“0..*”或“*”表示0个或多个。
4)“1..*”表示1个或多个。
5)“1,3,6”表示1个、3个或6个,属于枚举型符号,其中的数字并不固定,是根据类在关联关系中可以选择的数量来确定的。
虽然“0..*”和“1..*”都表示多个,但是前者表示类可以是0个,而后者表示类至少要有1个,两者不可以替换。图3-11显示了用重数符号表示的类之间的3种关联关系。
图3-11 类之间的关联关系
在类之间多对多的关联关系中,除了有数量关系的两个类外,通常还存在一个表明关系性质的类,称为关联类,它是具有类特征的关联或具有关联特征的类,关联类是用一条虚线连接到关联关系上。图3-11中的选修类就是学生和课程多对多关联关系所对应的关联类,它表明了学生和课程之间的关系性质。
2.聚集关系(Aggregation)
聚集关系表示类之间部分和整体的关系,是一种特殊的关联关系,即存在聚集关系的类之间除了数量对应关系外,其中一个类的对象还是另一个类的对象的一部分。聚集关系分为两种。
(1)共享聚集(Shared Aggregation)
代表部分的对象可以同时属于多个整体对象,为多个整体对象共享。如教师是学校的一部分,而一名教师可以同时在多个学校任职,则教师类和学校类之间是共享聚集的关系,如图3-12a所示。共享聚集用带空菱形的实线表示,菱形指向代表整体的类,由于共享聚集中的两个类是部分和整体的关系,而代表部分的对象又可以对应多个代表整体的对象,因此它们之间一定是多对多的关联关系。当代表整体的其中一个对象被从系统中删除时,代表部分的对象仍然存在。
(2)组合聚集(Composition Aggregation)
代表部分的对象只属于一个整体对象。如学生是班级的一部分,且通常一个学生只属于一个班级,则学生类和班级类之间是组合聚集的关系,如图3-12b所示。
组合聚集用带实菱形的实线表示,菱形指向代表整体的类,由于组合聚集中的两个类是部分和整体的关系,而代表部分的对象只属于一个代表整体的对象,因此它们之间一定是多对一的关联关系。当代表整体的某个对象被从系统中删除时,其所包含的代表部分的对象也就失去了在系统中存在的意义。
图3-12 类之间的聚集关系
a)共享聚集 b)组合聚集
3.依赖关系(Dependency)
依赖关系表示类之间存在调用关系,如当类A需要访问类B的属性和操作,或类A负责实例化类B时,则类A依赖类B,依赖关系多存在于控制类和实体类之间,用虚箭线表示,箭头指向被依赖的类。如图3-13所示的控制类“Login”在运行时需要调用实体类“User”中的“InquiringUser()”操作,它们之间存在依赖关系。
4.实现关系(Generalization)
实现关系是分类器之间的语义关系,一个分类器规定行为,另一个分类器负责实现这个行为,实现关系可以被用在接口与实现它们的类或组件中,也可以被用在用例和实现该用例的协作之间。在类图中,实现关系是指类与所对应接口之间的关系,接口可看成是一种只有操作、没有属性的特殊类,接口定义操作,类负责实现接口中的操作,实现关系用带空心三角形的虚线表示,三角形指向接口。如图3-14中UserOrder类负责实现Order接口。
图3-13 类之间的依赖关系
图3-14 类与接口间的实现关系
5.泛化关系(Realization)
泛化关系指的是类之间一般与特殊的关系,也即父与子的关系,子类继承父类所有的属性和操作,并且可以进行重新定义及增加新的属性和操作。泛化关系用带空心三角形的实线表示,三角形指向父类。前面提到,泛化关系在执行者之间、用例之间也存在。如图3-15所示,课程和基础课、课程和专业课之间是泛化关系,其中课程是父类,基础课及专业课是子类。
图3-15 类之间的泛化关系
3.4.5 类图的作用
根据组成结构及用途可将类图分为分析类图、边界类图和实体类图3种主要类型。
1)分析类图:由边界类、控制类和实体类3种类组成,用于在系统分析阶段描述用例执行过程,即指明参与用例的3种类及其之间的关系。
2)边界类图:指明系统中所有边界类及其之间的关系,可展现系统各界面之间的连接及跳转过程。
3)实体类图:指明系统中的各实体类及其之间的关系,由于实体类与数据库表是对应的,因此在设计阶段常用实体类图描述系统数据库中各表的结构及表间的关系。
3.4.6 建立类图的步骤
在系统分析与设计阶段,建立类图的步骤如下所示。
1)根据系统需求识别系统中的类。
2)根据系统性质确定各个类的属性。
3)结合类的属性及系统功能识别类的操作。
4)确定类之间的关系。
5)绘制并优化类图。
3.4.7 对象图
对象图(Object Diagram)表示一组对象及它们之间的关系,是类图在某一时刻的实例。在UML中,对象图使用的是与类图相同的符号和关系。
图3-16 对象图示例
虽然对象图是类图的实例,但是对象图和类图之间也存在明显的区别,如表3-2所示。
表3-2 类图和对象图的区别
案例3-2
“取款”是银行ATM系统最常见的业务,该功能的基本实现过程为客户在系统界面提出取款请求,系统根据界面传递来的客户请求进行取款处理,在验证取款金额的有效性后对账户余额进行相应更新,并添加一条取款交易记录。从这个过程中可得出与取款用例相关的类有:边界类“系统界面”,负责接收并传递客户的取款请求;控制类“取款”,负责处理“系统界面”传递来的取款请求,其行为包括查询账户余额并验证余额是否充足,在充足情况下更新账户余额并添加交易记录;实体类“账户”,负责提供查询账户余额和更新账户余额的方法;实体类“交易记录”,负责提供添加交易记录的方法。
根据上述过程描述可知,取款用例由执行者“客户”和4个类共同合作实现,它们之间的关系是“客户”和“系统界面”之间、“系统界面”和“取款”之间是关联关系,而控制类“取款”调用了实体类“账户”和“交易记录”的内部方法,故它们之间是依赖关系,最后可画出“取款”用例的分析类图,如图3-17所示。
图3-17 “取款”用例分析类图
案例3-3
如图3-18所示是银行ATM系统实体类图,包括账户(Account)和交易记录(Transac-tion Re cords)两个类,其中账户类有AccountID、UserID、UserName等多个私有属性,及addAcount()、removeAccount()、getAccount()等多个公有方法;交易记录类包括RecordID、AccountID、TransacNature、TransacTime等多个私有属性,及addRecord()、removeRecord()、getRecord()等多个公有方法;一个账户可以对应0到多条交易记录,而一条交易记录只属于一个账户,因此账户类和交易记录类之间是1对多的关联关系。
图3-18 银行ATM系统实体类图