什么是类图?
类图表示了程序的静态结构,会展示类与类之间的关系以及每一个类的内部关系。例如:在上一个部分中的Product类,如果使用类图来表示它的结构,就如图1所示,类的成员变量以及成员函数都可写到类图当中。
public
class
Product {
private
String
name
;
private
float
price
;
public
Product(String
name
,
float
price
){
this
.
name
=
name
;
this
.
price
=
price
;
}
public
String getName() {
return
name
;
}
public
void
setName(String
name
) {
this
.
name
=
name
;
}
public
float
getPrice() {
return
price
;
}
public
void
setPrice(
float
price
) {
this
.
price
=
price
;
}
}
图 1
下面,再用另一张图(图2)来讲述一下类图中类与类之间的关系以及这些关系如何进行表示。
图 2
(1)继承关系
在类图中的继承关系与人类社会的继承关系是很相似的。在人类社会中,子女继承父母的相貌上的特征,生活中的财产;在类的关系中,子类也继承父类中的成员变量与方法。例如:在图2中的“动物”这个类具有生命这个特征,同时还会“繁殖”和“新陈代谢”,“鸟”也是一种动物,因此它继承了“动物”的所有特征,同时,鸟还有自己的“羽毛”以及“下蛋”特性。在类中,继承使用带空心箭头的连线来表示:。
在java代码中用extends关键字来实现继承关系的:
Bird是鸟, Animal是动物,下面一句代码就是鸟继承了动物。
class Bird extends Animal{ }
(2)依赖关系
类图中的依赖关系,可以解释为一个类依赖于另一个类,就如图2中表示的“动物”依赖“空气”和“水”进行“新陈代谢”。在很多UML书中,依赖的解释就是类A使用到了类B,这种依赖具有偶然性、临时性,是非常弱的关系,但是类B的变化会影响到类A。我认为这样的解释非常的模糊,我在判断类A和类B是否是依赖关系的时候,使用一个非常直接的方法:第一是在现实的世界中,这两个类表示的实物是否具有依赖关系,第二是如果类B作为参数传入了类A中的任何一个方法中, 如果两个条件同时成立,那么类A就依赖类B。“依赖关系”使用带箭头虚线符号来表示:。
如果用代码来表示图2中的依赖关系,代码应该写成如下所示,在grow方法中,传入了Oxygen与Water对象作为参数。
氧气:oxygen、 水:water、 生命:life、 新陈代谢:grow
class
Water{ };
class
Oxygen{ };
class
Life{};
class
Animal{
private
Life
life
;
public
void
grow(Oxyen
O
, Water
water
){ };
}
(3)组合关系
在类图中的组合关系与现实中的组合关系也是一样的:就是由A组成了B, A是B的一个零
件,A与B是不可分割的,是部分和整体的关系,就如在图2中所示,“鸟”有两只“翅膀”,“翅膀”是“鸟”的组成部分。“组合”在类图中使用一个带实心菱形的连线表示:。
如果用代码来表示组合关系,一个关键的点就是类B需要使用类A来进行构造,A是B的一个零件,由A构成了B,因此,“鸟”由两只“翅膀”组成:
翅膀:Wing
class
Wing { };
class
Bird {
private
Wing[]
wings
=
new
Wing[2];
public
Bird(Wing
wings
){
this
.
wings
=
wings
;
}
}
(4)聚合关系
聚合关系与组合关系很类似,也是由类A组成了类B,但是区别在于类A不是类B的零件,类
B是由类A组成,但是类A是一个独立的完整的个体,就如图2中“大雁”和“雁群”的关系,大雁组成了雁群,但是大雁是独立的个体,少了一只大雁对于雁群的影响并不大。聚合关系在类图中是由一条带空心菱形的连线来表示的:。
如果用代码来表示图2“组合”关系,在类中应该有一个表示雁群的集合,并且可以在雁群中增加和减少大雁,构造函数可以使用大雁的集合来构造,也可以不用。如果不使用大雁的集合来构造雁群,需要有一个给雁群赋值的函数。
大雁:wild、 雁群:wild geese
class
Wild{ }
class
WildGeese{
private
List<Wild>
wilds
;
public
WildGeese(List<Wild>
wilds
) {
this
.
wilds
=
wilds
;
}
public
boolean
addWild(Wild
wild
) {
return
wilds
.add(
wild
);
}
public
boolean
removeWild(Wild
wild
) {
return
wilds
.remove(
wild
);
}
public
List<Wild> getWilds() {
return
wilds
;
}
public
void
setWilds(List<Wild>
wilds
) {
this
.
wilds
=
wilds
;
}
}
(5)关联关系
关联关系通俗一点来说就是在类A是类B的一个成员变量,类A的属性值会影响到类B,例如在图2中所示的,“气候”和“企鹅”是关联关系,气候的变化会影响到企鹅的生存,在类图中,关联关系由一个带普通箭头的连线来表示:。
用代码来表示关联的话,“气候”就是“企鹅”类的一个属性:
气候:climate、企鹅:penguin
class
Climate { };
class
Penguin {
private
Climate
climate
;
};
(6)实现关系
实现关系在类图中表示的是:如果B是一个抽象类或者是一个接口,类A实现了B中的抽象方法,那么类A和B的关系就是实现关系,例如图2中类“大雁”实现了“飞翔”这个接口。在类图中,实现关系由一个带空心三角形的虚线连线来表示:。
用代码来表示的话,示例代码如下:
interface
Fly{
public
abstract
void
fly();
}
class
Wild
implements
Fly{
public
void
fly() { };
}
类图就讲完了,那么什么又是时序图呢?
时序图是描述类对象之间发送消息的时间顺序以及对象之间的交互情况。 我在网上看到了一个非常有意思的使用时序图来描述赤壁之战的例子,修改了一下,放到文章中来解释一下时序图是怎么回事以及如何看时序图。
假设赤壁之战的过程是这样的:曹操向刘备下战书说:“我要打你”,刘备回复曹操说:“好,打就打,谁怕谁”?然后,刘备对诸葛亮说:“赶快制定一个可以赢曹操的计划”。诸葛亮连忙制定打仗的战略,一天之后,回复刘备:“我认为我们应该1. 向孙权求救,联合孙权;2.一定要守住荆州,3.放火船烧曹操的战船 ”。刘备按照诸葛亮建议,向孙权求救,孙权调集了3万人马联合刘备攻打曹操;刘备又命令关羽守住荆州,同时命令黄盖假装投降放船烧曹操的战船。最后的结果是:孙权大胜,关羽守住了荆州前线,黄盖也火烧赤壁,整个战争大胜,曹操逃跑了。这个过程画成时序图如下所示:
时序图画好了,如果将上面的时序图翻译成程序,应该如何写?
public
class
关羽
{
public
void
防守荊州
()
;
}
public
class
黄盖
{
public
void
借东风火攻
(
)(
);
}
public
class
孙权
{
public
void
领兵相助
()
;
}
public
class
刘备
{
public
String
打就打
() { }//回复曹操
public
void
应战
() {
//1.
调用诸葛亮拟定策略
//2.
得到策略之后开三个线程,一个线程找孙权求助,一个线程让黄盖火攻曹操战船
//
还有一个线程让关羽防守荆州
//3.
等到三个线程返回成功消息,通知曹操他战败了
}
}
public
class
诸葛亮
{
public
String
拟定策略
()
;
}
最后讲一下时序图中的符号的含义:
对象名生命线:表示对象的生存时间。生命线从对象创建开始到对象销毁时终止。有激活和休眠两种状态。如
果在生命线上有长方形的方块出现,就是激活状态。时序图中对象从左到右出现的顺序就是对象产生的顺序。
消息:对象之间的交互是通过相互发消息来实现的。一个对象可以请求(要求)另一个对象做某件事 件。
消息从源对象指向目标对象。消息一旦发送便将控制从源对象转移到目标对象。
激活状态: 这个长方形的方块放在对象的生命线上标明对象处于激活的状态,方块的长度就是对象处于激活状态的时 长。