您的位置:首页 > 其它

【转载】UML类图与类的关系详解 zt

2013-04-22 18:39 393 查看
文章来源: UML软件工程组织 ,作了很多整理,方便大家了解更全面。

摘要:类图(Class Diagram)可能是用得最多的一种UML图。类图的基本语法并不复杂,你可能最多学习两三天就可以掌握,然而要真正做到活用类图则可能需要几年的功力。类图是锻炼面向对象分析(OOA:Object-Oriented Analysis)和面向对象设计(OOD:Object-Oriented Design)思想的重要的工具,是业务结构建模的重要工具。本章将会有大量的实战练习,你的OOA思想将会接受极大的考验和提升。
大纲:
一、类的UML表示
二、类之间关系
三、演练类之间的关系
四、类关系小结
五、UML类图解义(类图实例)

一、类的UML表示 1、类的命名尽量应用领域中的术语,应明确、无歧义,以利于相互交流和理解。类的属性、操作中的可见性使用+、#、-分别表示public、protected、private。


2、抽象类和方法:
在类中定义的没有body 的方法称为抽象方法,至少有一个抽象方法的类是抽象类。使用斜体来描述:



3、接口: 和类表示差不多,在前边加上《》:


4、访问描述符: 我们用如下的符号表示访问修饰符:


我们在下图详细说明了各个访问修饰符的一些权限设定:


5、异常:
使用虚线以及箭头表示:


6、便签(包含限制、注释和代码解释):
只用这种dog-eared 的框框:


练习:你需要做一个培训管理系统,请你用类图识别出课室中有什么人?这些人有什么关键属性?
强烈建议你先独立完成才继续阅读下文。



图 1.8 学生与讲师1
说明:该图是类图的简单画法,只表达了类名。
这两个类有这样的关键属性:



图 1.9 学生与讲师2
说明:上面的类图同时表达了类名和类的属性。属性没有标记public还是private,也没有被标记属性的类型。业务建模时类图的属性可以看成全部是公开的,也不必标记属性的类型。
这个练习的场景是:你需要做一个培训管理系统,所以你识别出类以及他们的属性的时候,务必从这个角度出发。如果你得到的类是男人和女人,那就可能没有什么意义了。
如果你识别出来的属性是身高、体重,这些属性无论是属于学生还是老师,对于培训管理系统来说,可能是没有什么价值的。思考你识别出来的类的属性,能帮助你判断这个类是否合适。每一个类应该具备能表征它核心特点的关键属性,而一般的无特别意义的属性,可不必标记进去。
类图的基本语法是很简单的,但要体会什么是类,准确识别出类就不是那么简单了。实际工作中,我们需要将需求调研中了解到的所有业务对象、人物等列出来,画出他们的关系,反复推敲,逐步才能得到合适的业务模型。下面我们将开始学习类之间的关系。
二、类之间关系一个类图通常不止有一个类,有多个类时,我们还需要表达出类之间的关系。 类与类之间存在以下关系:
(1)泛化(Generalization)、实现(Realization)
(2)依赖(Dependency)
(3)关联(Association)
(4)聚合(Aggregation)、构成( Composition ) 1、泛化(Generalization:表示类与类之间的继承关系,接口与接口之间的继承关系,或类对接口的实现关系。一般化的关系是从子类指向父类的,与继承或实现的方法相反。 [具体表现] 父类 父类实例=new 子类();


图1.10 Animal类与Tiger类,Dog类的泛化关系 [代码表现] classAnimal{}
class Tiger extends Animal{}
public class Test
{
public void test()
{
Animal a=new Tiger();
}
}
2、实现(Realization,本质也是属于泛化(Generalization),因此也可以用泛化来描述实现
这是描述接口和具体实现的方式,在类图中使用带三角箭头的虚线表示,箭头从实现类指向接口。有两种表示方法:



或者:



3、依赖(Dependency) 对于两个相对独立的对象,当一个对象负责构造另一个对象的实例,或者依赖另一个对象的服务时,这两个对象之间主要体现为依赖关系。在类图使用带箭头的虚线表示,箭头从使用类指向被依赖的类。 依赖关系表现在局部变量,方法的参数,以及返回值的建立。 简单而言,依赖关系是一种局部使用关系。A类使用B类,则说明A类依赖于B类,图示如下:



A类在两种情况下使用B类: 1,A类负责构造B类的实例,即A类使用B类的构造器 【图示】

【代码】 package uml; public class CarFactory {
public Car makeNewCar(){
return new Car();
} } 2,A类使用B类实例的其他方法或者属性 【图示】


【代码】 package uml; public class Person {
public void drive2Office(Car myCar){
myCar.run();
} } 又比如:



4)关联(Association) 对于两个相对独立的对象,当一个对象的实例与另一个对象的一些特定实例存在固定的对应关系时,这两个对象之间为关联关系。 关联关系是使用实例变量来实现. 比如客户和订单,每个订单对应特定的客户,每个客户对应一些特定的订单;再例如公司和员工,每个公司对应一些特定的员工,每个员工对应一特定的公司 [UML图] (图1.11)


图1.11 公司和员工的关联关系(双向关联) [代码表现] publicclass Company{
private Employee employee;
public Employee getEmployee(){
return employee;
}
public void setEmployee(Employee employee){
this.employee=employee;
}
//公司运作
public void run(){
employee.startWorking();
}
}
关联关系是类(也可以说是对象)之间特定的对应关系。按照对象的数量对比,可以分为: A 一对一

比如公民和公民身份卡之间的对应关系。 B 一对多


一个部门对应0或者多位员工,一般而言一位员工只能属于某一个部门。 C 多对多


用户和服务是多对多的关系,一个用户可以注册0个或多个服务,一个服务则可以被0个或者多个用户复用。比如Windows Live用户可以激活邮件服务、Space服务等,而这些服务不是被一个用户所专有的。 关联的实质 从A类型到B类型的关联是指在A类型中定义了B类型作为属性。如下列代码: package uml; public class Citizen {
private CitizenshipCard card; //其他属性 public CitizenshipCard getCard() {
return card;
} public void setCard(CitizenshipCard card) {
this.card = card;
} } 上述代码演示了从Citizen 到 CitizenshipCard 的关联。注意下图箭头方向:


同样可以建立CitizenshipCard 到 Citizen 的关联:


代码表示为: package uml; public class CitizenshipCard {
private Citizen citizen; //其他属性 public Citizen getCitizen() {
return citizen;
} public void setCitizen(Citizen citizen) {
this.citizen = citizen;
} } 如果仅仅建立从Citizen 到 CitizenshipCard 的关联或者仅仅建立CitizenshipCard 到 Citizen 的关联,都属于单向关联,如果两个方向的关联都建立,就是双向关联:


是否建立双向关联要在实际项目中酌情而定。再看个双向关联的例子:


Department ----------------------------------------------------------------- package uml; import java.util.Set; public class Department {
private String name;
private Set<Employee> employees; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public Set<Employee> getEmployees() {
return employees;
} public void setEmployees(Set<Employee> employees) {
this.employees = employees;
} } Employee -------------------------------------------------------------------- package uml; public class Employee {
private String name;
private long joinTime;
private Department department; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public long getJoinTime() {
return joinTime;
} public void setJoinTime(long joinTime) {
this.joinTime = joinTime;
} public Department getDepartment() {
return department;
} public void setDepartment(Department department) {
this.department = department;
} } 表 3: 多重值和它们的表示
可能的多重值描述
表示含义
0..10个或1个
1只能1个
0..*0个或多个
*0个或多个
1..*1个或我个
3只能3个
0..50到5个
5..155到15个
5)聚合(Aggregation
UML表示法:空心菱形 + 实线 + 箭头



关系:" ... owns a ..."
聚合是强版本的关联。它暗含着一种所属关系以及生命期关系。被聚合的对象还可以再被别的对象关联,所以被聚合对象是可以共享的。虽然是共享的,聚合代表的是一种更亲密的关系。
典型的例子很多,比如:


图1.12 电脑和组件的聚合关系
[代码表现]
public class Computer{
private CPU cpu;
public CPU getCPU(){
return cpu;
}
public void setCPU(CPU cpu){
this.cpu=cpu;
}
//开启电脑
public void start(){
//cpu运作
cpu.run();
}
6)组合(Composition
UML表示法:实心菱形 + 实线 + 箭头



关系:" ... is a part of ..."
组合是关系当中的最强版本,它直接要求包含对象对被包含对象的拥有以及包含对象与被包含对象生命期的关系。被包含的对象还可以再被别的对象关联,所以被包含对象是可以共享的,然而绝不存在两个包含对象对同一个被包含对象的共享。
典型的例子很多,比如:
class Human
{
Heart myHeart = new Heart();
public static void main()
{
Human me = new Human();
while(true)
{
myHeart.beat();
}
}
}



释义:组合关系就是整体与部分的关系,部分属于整体,整体不存在,部分一定不存在,然而部分不存在整体是可以存在的,说的更明确一些就是部分必须创生于整体创生之后,而销毁于整体销毁之前。部分在这个生命期内可以被其它对象关联甚至聚合,但有一点必须注意,一旦部分所属于的整体销毁了,那么与之关联的对象中的引用就会成为空引用,这一点可以利用程序来保障。心脏的生命期与人的生命期是一致的,如果换个部分就不那么一定,比如阑尾,很多人在创生后的某个时间对其厌倦便提前销毁了它,可它和人类的关系不可辩驳的属于组合。
在UML中存在一种特例,就是允许被包含对象在包含对象销毁前转移给新的对象,这虽然不自然,但它给需要心脏移植的患者带来了福音。
关联与聚合的区别: (1)关联关系所涉及的两个对象是处在同一个层次上的。比如人和自行车就是一种关联关系,而不是聚合关系,因为人不是由自行车组成的。聚合关系涉及的两个对象处于不平等的层次上,一个代表整体,一个代表部分。比如电脑和它的显示器、键盘、主板以及内存就是聚合关系,因为主板是电脑的组成部分。
(2)对于具有聚合关系(尤其是强聚合关系)的两个对象,整体对象会制约它的组成对象的生命周期。部分类的对象不能单独存在,它的生命周期依赖于整体类的对象的生命周期,当整体消失,部分也就随之消失。比如张三的电脑被偷了,那么电脑的所有组件也不存在了,除非 张三事先把一些电脑的组件(比如硬盘和内存)拆了下来。
关联、聚合和构成的区别: 关联关系、聚合关系和构成关系,都属于广义的关联关系,三种关系的在面向对象的编程语言中表现方式是一致的。但是它们的本质区别是存在的:
实质元素生命周期举例
关联关系所属关系彼此独立用户与订单
聚合关系灵活平台分享彼此独立主板与内存条
构成关系强制平台分享“国破家亡”身体和脑袋;脑袋和耳朵
三、演练类之间的关系
练习1、2、3是简单的小练习,而练习4的难度会有所增加。这些练习不仅仅是让你巩固上小节学习的知识,中间还会穿插一些前面还没有介绍的基础知识,而且会让你体验什么是面向对象分析,领悟用类图分析需求的要诀。你准备好接受挑战没有?
练习1:你和你另外一半的关系
你结婚了吗?如果你已婚,那么请你用类图描绘你和你的另外一半的关系?
如果你是单身的,你有男朋友或女朋友吗?有的话,请你用类图画出你们两人的关系?
如果你还没有另外一半,而你又已经到了适合恋爱的年龄,那请你虚拟一位你的意中人,用类图画出你和你的虚拟意中人的关系。
如果你还没有到恋爱或结婚年龄,那么你不需要完成这个练习,直接看后面的参考答案。
如果你是已婚人士,那么你们的关系应该是:



图 1.20 你和你的另外一半关系1
如果你是男生,你在这个关系中的角色就是老公,如果你是女生你就是老婆。一个老公只能对应一个老婆,你应该不会画出1对多吧?
这个图也可以画成这样:



图 1.21 你和你的另外一半关系2
这个图在直线上面的“夫妻关系”表示这个关系的名称,你可以为关联关系命名,但这不是必须的,在需求分析工作中也很少有这种需要。
如果你未婚,但你同时有多个男朋友或者女朋友,那么你们的关系可以这样表示:



图 1.22 你和你的另外一半关系3
“1..*”表示1到多个,不要因为你能1对多个男朋友(或女朋友)就很开心,这是一种很不好的关系,强烈建议你将1对多的关系变为1对1,而且说不定有朝一日你会被别人1对多。
如果你还没有另外一半,你可以画成这样:



图 1.23 你和你的另外一半关系3
你的另外一半是作为“虚拟情人”存在的。
如果你很爱你的另外一半,你依赖于你的另外一半,没有她(他)你简直不能活,她(他)是你的生存必需品,你可以画成这样:



图 1.24 你和你的另外一半关系4
你可以跟你的另外一半画画这个图,跟她(他)解释一下是什么意思,你的另外一半一定开心死了。
用类图表达你和你的另外一半的关系,并没有固定的标准答案,你画出来的可能跟上述的参考答案不一样,只要你的逻辑正确,这个图也就是合适的。
下面介绍读图检查法,能帮助你检查类图画得是否合适。
你可以分别从左到右、从右到左来读图,看看有没有不合理的地方。以图1.22为例,从左到右读:1个你对应1个到多个你的另外一半。从右到左读:1个你的 另外一半对应1个你,而不要读成:多个你的另外一半对应1个你。注意由“多”的一边往另外一边读时,仍然是1个什么对应多少个什么,无论你从哪边开始读 起,都是以“1个……”开头。
练习2:公司与雇员的关系
前面学习了部门与员工的关系,公司与雇员是怎样的关系呢?请用类图画出来。



图 1.25 公司与雇员的关系
这个图表示公司“包含”多名员工,而公司这边也有一个“*”号,这表示一名雇员可受雇于多个公司。事实上很多公司是禁止员工同时受雇于另外一个公司或者是兼职的,这样公司这边就不能画“*”号。
这里的包含是弱包含,能不能画成强包含呢?公司如果不存在了,雇员还存在吗?一个公司没有了,这个公司应该就不会有任何雇员,但不代表原来的雇员都消失 了,他们还是存在的。这个问题就比较纠结了,到底是弱包含还是强包含,每个人的标准可能不一样,我不建议在弱包含还是强包含上过于纠结,我做需求分析时绝 大部分情况只会用弱包含,强包含只会在很明显的情况下才用。
练习3:香蕉、苹果、梨子的关系
你吃过香蕉、苹果和梨子吗?这三个东西有怎样的关系?请用类图画出来。
你可能觉得这个练习有点“无厘头”,这三种水果能有怎样的关系?它们无非都是可以吃的罗!



图 1.26 香蕉、苹果、梨子的关系
此图表示香蕉、苹果、梨子都是水果的一种,这就是这三者的关系。 用专业一点的说法就是香蕉、苹果、梨子泛化为水果。和前面提到的老师、学生泛化为员工不一样,员工是确实存在的,而水果只是一种泛称,没有一样东西的名字 直接叫水果的,我们见到的水果都是具体的一种水果。泛化以后的类,有可能是一种经过“抽象”后的东西,这个东西是看不到摸不着的,是我们脑袋里面提炼出来 的一种概念。
香蕉、苹果、梨子泛化为水果,水果可以再泛化为食物,食物又可以进一步泛化。有没有必要不断泛化呢?泛化到怎样的程度才是合适的呢?一般来说,如果有A、 B、C等两个或者以上的业务概念,我们发现它们有一些共同的特征,则可以考虑将它们泛化为另外一个东西, 这样能帮助我们发现食物的本质;但如果只有一个A时,就没有必要对A再进行泛化,例如:香蕉、苹果、梨子已经泛化为水果了,而水果则没有必要泛化为食物。 当然这只是一般准则,具体要泛化到怎样的层次要看具体的业务分析需要,要靠你自己来把握。
练习4:公司的组织架构
这个练习开始有点复杂了,请你用类图描述你所在公司的组织架构。如果你们公司比较庞大,你不是很了解整个公司的组织架构,那么请你选择你熟悉的部分用类图来描述它的组织架构。如果你是学生,那么请你描述你所在大学、学院或学系的组织架构。
我们可以用组织架构图来描绘组织架构,为什么要用类图来表达呢?组织架构图画起来很方便,用类图的画反而觉得有点别扭,用类图来表达组织架构,是不是应该有更大的好处呢?请你带着这些问题来完成这个练习。
某公司只是一个中小型的公司,该公司由一个一个的部门组成,用类图表达其组织架构可能是这样的:



图 1.27 公司的组织架构1
该公司有一个行政人事部、一个研发部、一个服务部、一个销售部、一个财务部。这个图似乎公司有多少个部门,就多画一个包含就搞定了,这样画似乎一点都显示不出类图的优势。
下面这种画法又如何呢?



图 1.28 公司的组织架构2
注意图中抽象部门这四个字是斜体字,这表明这个类是抽象类(Abstract Class),抽象类表示这个类是提炼出来的一种概念,是不具体存在的,具体存在的是继承抽象部门的各个具体的部门。
前面提到的香蕉、苹果、梨子泛化为水果,水果其实也是一种抽象的概念,前面那个图的水果可以画成抽象类。
这个组织架构图已经一定程度地揭示了公司组织架构的本质,一个公司无非就是由一个个部门组成的,只是每个公司具体的部门可能不一样而已。这样的表达效果,用普通的组织架构图是表达不出来的,而类图就可以发挥抽象和提炼的优势。
下面这个图将更进一步揭示公司组织架构的本质:



图 1.29 公司的组织架构3
公司由一个个的部门组成,但要构成一个完整的公司,这些部门应该分为三类:
市场类部门:负责公司形象推广、产品营销方面的部门。
生产类部门:直接生产公司产品的部门。
支持类部门:不直接生产公司产品,但是支持产品生产或支撑公司运作必不可少的部门。
在这个图中,市场类部门有策划部、销售部,生产类部门有研发部、实施部、IT部,支持类部门有:IT部、质量部、财务部、行政人事部,其中IT部既是生产类部门,也是支持类部门。
下面对其中一些具体部门进行解释:
实施部是负责将软件系统安装到客户现场,保障系统上线运行的部门。
IT部主要负责两方面的职能,一方面要保障公司内部的办公软硬件环境,另一方面会承接一些外部的网络工程,为公司直接盈利。第一方面的工作是属于支持类方面的工作,而第二方面的工作则是生产类的工作。
质量部负责测试及过程保障的工作,这个部门是支援研发部和实施部工作的,故也属于支持类的部门。
将部门分为市场类、生产类和支持类,只是其中一种的抽象方法,每个人可能会有不同的标准,遇到不同情况可能会有不同的抽象办法。以上这个仅是一个例子,你千万不要将其当成一个固定的标准。
总体来说,上述三个用类图表示的公司组织架构,所针对的公司都不 是大型的公司,大型的公司可能会有分公司、子公司、事业部等等不同的划分办法,组织架构异常复杂,想用类图准确地表达出来并且能揭示其本质相当不容易。希 望通过上述三个例子,能让你初步体会用类图提炼业务的优势。
上面四个练习,基本覆盖了你在前面小节学习到的类之间关系的知识。在我的经验看来,直线(关联)关系、包含关系是最常用的,泛化(继承)关系用得也比较多,而依赖关系用得不是很多。而从使用的难度来说,泛化(继承)关系是最考验人的了,很考验你发掘事物本质的能力。

四、小结
类图是最常用的UML图,是用来训练你OOA思想的最好武器。类图的语法不算很难,要看懂类图难度不大,但要用好类图就相当不容易了。以下是类关系图小结。



五、UML类图解义(类图实例)(来源: 作者:HeroBeast, 来源:海尔比斯特's Blogs )
小菜:“对了,我时常在一些技术书中看到这些类图表示,简单的还看得懂,有些标记我很容易混淆。要不你给我讲讲吧。” 大鸟:“这个其实多看多用就熟悉了。我给你举一个例子,来看这样一幅图,其中就包括了UML类图中的基本图示法。” UML类图图示样例

大鸟:“首先你看那个‘动物’矩形框,它就代表一个类(Class)。类图分三层,第一层显示类的名称,如果是抽象类,则就用斜体显示。第二层是类的特性,通常就是字段和属性。第三层是类的操作,通常是方法或行为。注意前面的符号,‘+’表示public,‘-’表示private,‘#’表示protected。”

大鸟:“然后注意左下角的‘飞翔’,它表示一个接口图,与类图的区别主要是顶端有<<interface>>显示。第一行是接口名称,第二行是接口方法。接口还有另一种表示方法,俗称棒棒糖表示法,就是唐老鸭类实现了‘讲人话’的接口。” 小菜:“为什么要是‘讲人话’?” 大鸟:“鸭子本来也有语言,只不过只有唐老鸭是能讲人话的鸭子。” 小菜:“有道理。”




大鸟:“接下来就可讲类与类,类与接口之间的关系了。你可首先注意动物、鸟、鸭、唐老鸭之间关系符号。” 小菜:“明白了,它们都是继承的关系,继承关系用空心三角形+实线来表示。”

大鸟:“我举的几种鸟中,大雁是最能飞的,我让它实现了飞翔接口。实现接口用空心三角形+虚线来表示。”



大鸟:“你看企鹅和气候两个类,企鹅是很特别的鸟,会游不会飞。更重要的是,它与气候有很大的关联。我们不去讨论为什么北极没有企鹅,为什么它们要每年长途跋涉。总之,企鹅需要‘知道’气候的变化,需要‘了解’气候规律。当一个类‘知道’另一个类时,可以用关联(association)。关联关系用实线箭头来表示。”



大鸟:“我们再来看大雁与雁群这两个类,大雁是群居动物,每只大雁都是属于一个雁群,一个雁群可以有多只大雁。所以它们之间就满足聚合(Aggregation)关系。聚合表示一种弱的‘拥有’关系,体现的是A对象可以包含B对象,但B对象不是A对象的一部分[DPE](DPE表示此句摘自《设计模式》(第2版),详细摘要说明见附录二)。聚合关系用空心的菱形+实线箭头来表示。”



大鸟:“合成(Composition,也有翻译成‘组合’的)是一种强的‘拥有’关系,体现了严格的部分和整体的关系,部分和整体的生命周期一样[DPE]。在这里鸟和其翅膀就是合成(组合)关系,因为它们是部分和整体的关系,并且翅膀和鸟的生命周期是相同的。合成关系用实心的菱形+实线箭头来表示。另外,你会注意到合成关系的连线两端还有一个数字‘1’和数字‘2’,这被称为基数。表明这一端的类可以有几个实例,很显然,一个鸟应该有两只翅膀。如果一个类可能有无数个实例,则就用‘n’来表示。关联关系、聚合关系也可以有基数的。”

class Bird { private Wing wing; public Bird() { wing = new Wing(); } } 大鸟:“动物几大特征,比如有新陈代谢,能繁殖。而动物要有生命力,需要氧气、水以及食物等。也就是说,动物依赖于氧气和水。他们之间是依赖关系(Dependency),用虚线箭头来表示。”

abstract class Animal { public Metabolism (Oxygen oxygen,Water water) { } } 小菜:“啊,看来UML类图也不算难呀。回想那天我面试题写的代码,我终于明白我为什么写得不成功了,原来一个小小的计算器也可以写出这么精彩的代码,谢谢大鸟。” 大鸟:“吼吼,记住哦,编程是一门技术,更加是一门艺术,不能只满足于写完代码运行结果正确就完事,时常考虑如何让代码更加简练,更加容易维护,容易扩展和复用,只有这样才可以真正得到提高。写出优雅的代码真的是一种很爽的事情。UML类图也不是一学就会的,需要有一个慢慢熟练的过程。所谓学无止境,其实这才是理解面向对象的开始呢。”

PS:还是那句话:以上类图用Enterprise Architect 7.5所画,在此推荐一下EA,非常不错。可以替代Visio和Rose了。Visio功能不够强大,Rose太重。唯有EA比较合适。

更多信息,请参考 UML软件工程组织分析业务模型-类图(Class Diagram)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: