地址:
看懂UML类图
这里不会将UML的各种元素都提到,我只想讲讲类图中各个类之间的关系; 能看懂类图中各个类之间的线条、箭头代表什么意思后,也就足够应对 日常的工作和交流; 同时,我们应该能将类图所表达的含义和最终的代码对应起来; 有了这些知识,看后面章节的设计模式结构图就没有什么问题了;
UML类图表示
在UML类图中表示具体类
具体类在类图中用矩形框表示,矩形框分为三层:
- 第一层是类名字。
- 第二层是类的成员变量;
- 第三层是类的方法。
成员变量以及方法前的访问修饰符用符号来表示:
- “+”表示
public
; - “-”表示
private
; - “#”表示
protected
; - 不带符号表示
default
。
在UML类图中表示抽象类
抽象类在UML类图中同样用矩形框表示,但是抽象类的类名以及抽象方法的名字都用斜体字表示,如图2所示。
在UML类图中表示接口
接口在类图中也是用矩形框表示,但是与类的表示法不同的是,接口在类图中的第一层顶端用构造型 <<interface>>
表示,下面是接口的名字,第二层是方法,如图3所示。此外,接口还有另一种表示法,俗称棒棒糖表示法,就是类上面的一根棒棒糖(圆圈+实线)。圆圈旁为接口名称,接口方法在实现类中出现。
在UML类图中表示包
类和接口一般都出现在包中,UML类图中包的表示形式如图4所示。
从一个示例开始
请看以下这个类图,类之间的关系是我们需要关注的:
- 车的类图结构为
<<abstract>>
,表示车是一个抽象类; - 它有两个继承类:小汽车和自行车;它们之间的关系为
实现关系
,使用带空心箭头的虚线表示; - 小汽车为与SUV之间也是继承关系,它们之间的关系为
泛化关系
,使用带空心箭头的实线表示; - 小汽车与发动机之间是
组合关系
,使用带实心箭头的实线表示; - 学生与班级之间是
聚合关系
,使用带空心箭头的实线表示; - 学生与身份证之间为
关联关系
,使用一根实线表示; - 学生上学需要用到自行车,与自行车是一种
依赖关系
,使用带箭头的虚线表示;
下面我们将介绍这六种关系;
类之间的关系
泛化关系(generalization)
类的继承结构表现在UML中为:泛化(generalize)
与实现(realize)
:
继承关系为 is-a的关系;两个对象之间如果可以用 is-a 来表示,就是继承关系:(..是..)
eg:自行车是车、猫是动物
泛化关系用一条带空心箭头的直接表示;如下图表示(A继承自B);
eg:汽车在现实中有实现,可用汽车定义具体的对象;汽车与SUV之间为泛化关系;
注:最终代码中,泛化关系表现为继承非抽象类;
泛化关系(Generalization)是指对象与对象之间的继承关系。如果对象A和对象B之间的“is a”关系成立,那么二者之间就存在继承关系,对象B是父对象,对象A是子对象。例如,一个年薪制员工“is a”员工,很显然年薪制员工Salary对象和员工Employee对象之间存在继承关系,Employee对象是父对象,Salary对象是子对象。
在UML类图中,泛化关系用空心三角和实线组成的箭头表示,从子类指向父类,如图8所示。在Java代码中,对象之间的泛化关系可以直接翻译为关键字
extends
。
实现关系(realize)
实现关系用一条带空心箭头的虚线表示;
eg:”车”为一个抽象概念,在现实中并无法直接用来定义对象;只有指明具体的子类(汽车还是自行车),才 可以用来定义对象(”车”这个类在C++中用抽象类表示,在JAVA中有接口这个概念,更容易理解)
注:最终代码中,实现关系表现为继承抽象类;
实现关系是指接口及其实现类之间的关系。在UML类图中,实现关系用空心三角和虚线组成的箭头来表示,从实现类指向接口,如图1.9所示。在Java代码中,实现关系可以直接翻译为关键字
implements
。
聚合关系(aggregation)
聚合关系用一条带空心菱形箭头的直线表示,如下图表示A聚合到B上,或者说B由A组成;
聚合关系用于表示实体对象之间的关系,表示整体由部分构成的语义;例如一个部门由多个员工组成;
与组合关系不同的是,整体和部分不是强依赖的,即使整体不存在了,部分仍然存在;例如, 部门撤销了,人员不会消失,他们依然存在;
聚合(Aggregation)是关联关系的一种特例,它体现的是整体与部分的拥有关系,即 “has a” 的关系。此时整体与部分之间是可分离的,它们可以具有各自的生命周期,部分可以属于多个整体对象,也可以为多个整体对象共享,所以聚合关系也常称为共享关系。例如,公司部门与员工的关系,一个员工可以属于多个部门,一个部门撤消了,员工可以转到其它部门。
在UML图中,聚合关系用空心菱形加实线箭头表示,空心菱形在整体一方,箭头指向部分一方,如图11所示。
组合关系(composition)
组合关系用一条带实心菱形箭头直线表示,如下图表示A组成B,或者B由A组成;
与聚合关系一样,组合关系同样表示整体由部分构成的语义;比如公司由多个部门组成;
但组合关系是一种强依赖的特殊聚合关系,如果整体不存在了,则部分也不存在了;例如, 公司不存在了,部门也将不存在了;
组合(Composition)也是关联关系的一种特例,它同样体现整体与部分间的包含关系,即 “contains a” 的关系。但此时整体与部分是不可分的,部分也不能给其它整体共享,作为整体的对象负责部分的对象的生命周期。这种关系比聚合更强,也称为强聚合。如果
A
组合B
,则A
需要知道B
的生存周期,即可能A
负责生成或者释放B
,或者A
通过某种途径知道B
的生成和释放。例如,人包含头、躯干、四肢,它们的生命周期一致。当人出生时,头、躯干、四肢同时诞生。当人死亡时,作为人体组成部分的头、躯干、四肢同时死亡。
在UML图中,组合关系用实心菱形加实线箭头表示,实心菱形在整体一方,箭头指向部分一方,如图12所示。
在Java代码形式上,聚合和组合关系中的部分对象是整体对象的一个成员变量。但是,在实际应用开发时,两个对象之间的关系到底是聚合还是组合,有时候很难区别。在Java中,仅从类代码本身是区分不了聚合和组合的。如果一定要区分,那么如果在删除整体对象的时候,必须删掉部分对象,那么就是组合关系,否则可能就是聚合关系。从业务角度上来看,如果作为整体的对象必须要部分对象的参与,才能完成自己的职责,那么二者之间就是组合关系,否则就是聚合关系。
例如,汽车与轮胎,汽车作为整体,轮胎作为部分。如果用在二手车销售业务环境下,二者之间就是聚合关系。因为轮胎作为汽车的一个组成部分,它和汽车可以分别生产以后装配起来使用,但汽车可以换新轮胎,轮胎也可以卸下来给其它汽车使用。如果用在驾驶系统业务环境上,汽车如果没有轮胎,就无法完成行驶任务,二者之间就是一个组合关系。再比如网上书店业务中的订单和订单项之间的关系,如果订单没有订单项,也就无法完成订单的业务,所以二者之间是组合关系。而购物车和商品之间的关系,因为商品的生命周期并不被购物车控制,商品可以被多个购物车共享,因此,二者之间是聚合关系。
关联关系(association)
关联关系是用一条直线表示的;它描述不同类的对象之间的结构关系;它是一种静态关系, 通常与运行状态无关,一般由常识等因素决定的;它一般用来定义对象之间静态的、天然的结构; 所以,关联关系是一种“强关联”的关系;
比如,乘车人和车票之间就是一种关联关系;学生和学校就是一种关联关系;
关联关系默认不强调方向,表示对象间相互知道;如果特别强调方向,如下图,表示A知道B,但 B不知道A;
注:在最终代码中,关联对象通常是以成员变量的形式实现的;
关联关系(Association)是指对象和对象之间的连接,它使一个对象知道另一个对象的属性和方法。在Java中,关联关系的代码表现形式为一个对象含有另一个对象的引用。也就是说,如果一个对象的类代码中,包含有另一个对象的引用,那么这两个对象之间就是关联关系。
关联关系有单向关联和双向关联。如果两个对象都知道(即可以调用)对方的公共属性和操作,那么二者就是双向关联。如果只有一个对象知道(即可以调用)另一个对象的公共属性和操作,那么就是单向关联。大多数关联都是单向关联,单向关联关系更容易建立和维护,有助于寻找可重用的类。
在UML图中,双向关联关系用带双箭头的实线或者无箭头的实线双线表示。单向关联用一个带箭头的实线表示,箭头指向被关联的对象,如图9所示。这就是导航性(Navigatity)。
一个对象可以持有其它对象的数组或者集合。在UML中,通过放置多重性(multipicity)表达式在关联线的末端来表示。多重性表达式可以是一个数字、一段范围或者是它们的组合。多重性允许的表达式示例如下:
- 数字:精确的数量
*
或者0..*
:表示0到多个0..1
:表示0或者1个,在Java中经常用一个空引用来实现1..*
:表示1到多个关联关系又分为依赖关联、聚合关联和组合关联三种类型。
依赖关系(dependency)
依赖关系是用一套带箭头的虚线表示的;如下图表示A依赖于B;他描述一个对象在运行期间会用到另一个对象的关系;
与关联关系不同的是,它是一种临时性的关系,通常在运行期间产生,并且随着运行时的变化; 依赖关系也可能发生变化;
显然,依赖也有方向,双向依赖是一种非常糟糕的结构,我们总是应该保持单向依赖,杜绝双向依赖的产生;
注:在最终代码中,依赖关系体现为类构造方法及类方法的传入参数,箭头的指向为调用关系;依赖关系除了临时知道对方外,还是“使用”对方的方法和属性;
依赖(Dependency)关系是一种弱关联关系。如果对象A用到对象B,但是和B的关系不是太明显的时候,就可以把这种关系看作是依赖关系。如果对象A依赖于对象B,则 A “use a” B。比如驾驶员和汽车的关系,驾驶员使用汽车,二者之间就是依赖关系。
在UML类图中,依赖关系用一个带虚线的箭头表示,由使用方指向被使用方,表示使用方对象持有被使用方对象的引用,如图10所示。
依赖关系在Java中的具体代码表现形式为B为A的构造器或方法中的局部变量、方法或构造器的参数、方法的返回值,或者A调用B的静态方法。
下面我们用代码清单1和代码清单2所示的Java代码来演示对象和对象之间的依赖关系。
代码清单1所示的
B
类定义了一个成员变量field1
,一个普通方法method1()
和一个静态方法method2()
。
1
2
3
4
5
6
7
8
9
10
11
12 //代码清单1 B.java
public class B {
public String field1; //成员变量
public void method1() {
System.println("在类B的方法1中");
}
public static void method2() { //静态方法
System.out.println("在类B的静态方法2中");
}
}代码清单2所示的
A
类依赖于B
类,在A
类中定义了四个方法,分别演示四种依赖形式。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 /* 代码清单2 A.java
A依赖于B
*/
public class A {
public void method1() {
//A依赖于B的第一种表现形式:B为A的局部变量
B b = new B();
b.method1();
}
public void method2() {
//A依赖于B的第二种表现形式: 调用B的静态方法
B.method2();
}
public void method3(B b) {
//A依赖于B的第三种表现形式:B作为A的方法参数
String s = b.field1;
}
//A依赖于B的第四种表现形式:B作为A的方法的返回值
public B method4() {
return new B();
}
}