首页 > 代码库 > UML中类图的四种关系及其代码实现
UML中类图的四种关系及其代码实现
在uml图中 最复杂的也就是泛化,实现,依赖,关联,这四种关系了,如果弄清了这几种关系那么在理解UML图的时候就会变得轻车熟路了!
如果你对着几种关系一点都不熟悉的话可以看一下uml中的四种关系,这篇博客简单的介绍了一下这几种关系,本文将重点的介绍一下,这几种关系在代码里如何实现的!
泛化关系
我想这个也可能是最简单的关系了,泛化就是特殊到一半的过程,也就是继承的相反的过程,子类继承自父类,而父类是从子类泛化而来!
泛化(generalization)关系是一个类(称为子类、子接口)继承另外的一个类(称为父类、父接口)的功能,并可以增加它自己的新功能的能力,继承是类与类或者接口与接口之间最常见的关系;在Java中此类关系通过关键字extends明确标识,在c#中用:来表示。在设计时一般没有争议性。
<span style="font-size:18px;">namespace DEMO { //类继承类 class ClassB: ClassA { } //接口继承接口 interface InterfaceB: InterfaceA { } }</span>
实现关系
实现(realization)关系指的是一个class类实现interface接口(可以是多个)的功能;实现是类与接口之间最常见的关系;在Java中此类关系通过关键字implements明确标识,在c#中用“:”表示在设计时一般没有争议性;
<span style="font-size:18px;">namespace DEMO { //类实现接口 class ClassC:InterfaceC{ } }</span>
依赖关系
依赖(dependency)关系: 也是类与类之间的连接. 表示一个类依赖于另一个类的定义. 依赖关系总是单向的 。可以简单的理解,就是一个类A使用到了另一个类B,而这种使用关系是具有偶然性的、、临时性的、非常弱的,但是B类的变化会影响到A;比如某人要过河,需要借用一条船,此时人与船之间的关系就是依赖;表现在代码层面,为类B作为参数被类A在某个method方法中使用。或者在方法体重声明该类的变量,或者直接引用该类!
public class Boat { /// <summary> /// 过河 /// </summary> public voidriverCrossing(){ //启动 //过河 } public staticvoid riverCrossingByShip(){ //用大 船过河 } }//end Boat public class Person { //通过参数的形式用Bote 类 //Bote类被Person类的一个方法所持有,生命周期随着方法执行结束而结束。 /// <summary> /// 过河 /// </summary> /// <paramname="bote">船的实例</param> public voidriverCrossing(Boat bote){ bote.riverCrossing(); } //在方法体重生命该类的变量 //注意Bote类的生命周期,当riverCrossing方法被调用的时候,才被实例化。 //持有Bote类的是Person类的一个方法,而不是Person类,这点是最重要的。 public voidriverCrossing() { Boatbote = new Boat(); bote.riverCrossing(); } //直接应用该类 public voidriverCrossing() { Boat.riverCrossingByShip(); } }//end Person
关联关系
关联关系是实线加箭头表示。表示类之间的关系比依赖要强。
例如,水和气候是关联的,表示如下:
在代码中的表现如下:
<span style="font-size:18px;">namespace DEMO { /// <summary> /// 水 /// </summary> class Water { //吧气候类 的实例作为该类的一个变量! public Climateclimate; publicWater() { } } /// <summary> /// 气候 /// </summary> class Climate { publicClimate() { } } }</span>
可见,在Water类属性中增加了Climate类。
关联关系有单向关联、双向关联、自身关联、多维关联等等。其中后三个可以不加箭头。
单向关联:
双向关联:
自身关联:
多维关联:
关联和依赖的区别:
从类的属性是否增加的角度看:
发生依赖关系的两个类都不会增加属性。其中的一个类作为另一个类的方法的参数或者返回值,或者是某个方法的变量而已。
发生关联关系的两个类,其中的一个类成为另一个类的属性,而属性是一种更为紧密的耦合,更为长久的持有关系。
从关系的生命周期来看:
依赖关系是仅当类的方法被调用时而产生,伴随着方法的结束而结束了。
关联关系是当类实例化的时候即产生,当类销毁的时候,关系结束。相比依赖讲,关联关系的生存期更长。
关联关系按关系强弱右分为聚合 和组合 ,下边用大雁和雁群的例子讲解一下!
大雁喜欢热闹害怕孤独,所以它们一直过着群居的生活,这样就有了雁群,每一只大雁都有自己的雁群,每个雁群都有好多大雁,大雁与雁群的这种关系就可以称之为聚合。
另外每只大雁都有两只翅膀,大雁与雁翅的关系就叫做组合。
由此可见:
聚合的关系明显没有组合紧密,大雁不会因为它们的群主将雁群解散而无法生存;
而雁翅就无法脱离大雁而单独生存——组合关系的类具有相同的生命周期。
聚合关系图:
组合关系图:
在代码中表现如下:
<span style="font-size:18px;">namespace DEMO { /// <summary> /// 雁群 /// </summary> class GooseGroup { public Goosegoose; publicGooseGroup(Goose goose) { this.goose= goose; } } /// <summary> /// 大雁 /// </summary> class Goose { publicWing wing; publicGoose() { wing = new Wing(); } } }</span>
这两种关系的区别是:
1.构造函数不同
聚合类的构造函数中包含另一个类的实例作为参数
因为构造函数中传递另一个类的实例,因此大雁类可以脱离雁群类独立存在。
组合类的构造函数包含另一个类的实例化
因为在构造函数中进行实例化,因此两者紧密耦合在一起,同生同灭,翅膀类不能脱离大雁类存在。
2.信息的封装性不同
在聚合关系中,客户端可以同时了解GooseGroup类和Goose类,因为他们是独立的。
在组合关系中,客户端只认识大雁类,根本不知道翅膀类的存在,因为翅膀类被严密地封装在大雁类中。
总结:
对于继承、实现这两种关系没多少疑问,他们体现的是一种类与类、或者类与接口间的纵向关系;其他的四者关系则体现的是类与类、或者类与接口间的引用、横向关系,是比较难区分的,有很多事物间的关系要想准备定位是很难的,前面也提到,这几种关系都是语义级别的,所以从代码层面并不能完全区分各种关系;但总的来说,后几种关系所表现的强弱程度依次为:组合>聚合>依赖。
UML中类图的四种关系及其代码实现