谈谈UML中类图之间的关系

我们在用UML建模语言做软件设计的时候,通常会用到UML的类图,而这个类图之间的关系很多时候却给大家带来了不必要的麻烦和疑惑,尤其是对于组合与聚合等这样的关系不太好把握,在这里我谈一下我的理解和应用。

UML中将类与类之间的关系细分为这样几种关系,分别是: 关联、聚合/组合、依赖、泛化(继承)。正因为这样的细分,导致我们在使用的时候,显得比较迷茫,并且由于在做UML时,不可避免的与某一种开发语言进行联系,更加导致了对类图之间的关系不好把握了。比如对于组合与聚合之间的关系,主要表现为一个对象是否对其他对象进行生命周期的管理,在C++中,我们需要人为的进行对象生命周期的管理,而在JAVA语言中,却因为存在于JAVA虚拟机,而虚拟机可以帮我们管理对象的生命周期,所以如果是JAVA的设计人员来进行类图关系设计时,就会更加对组合与聚合的关系把握不准了。

其实我们在做类之间的关系时,往往忽略了一点,就是类本身就是抽象意义上的概念,而所谓的类之间的关系,通常是为了表述在类的实例化对象时,对象之间的关系。所以会给人觉得,类的关系到底是指的是类之间的关系还是指的是类的实例化对象之间的关系。对于这样的问题,我觉得既然是指的是类之间的关系,就不存在实例化对象之间的关系。只要把类之间的关系把握好了,那么实例化的对象之间的关系自然而然地形成。

我个人的理解是这样的,类的关系只有两种,就是横向与纵向的关系。所谓横向指的是如关联,组合/聚合,依赖这四种关系,他们看起来更像是横向对比的概念上引出的,而纵向指的是继承与实现接口(以及实现抽象类)这样的关系。

下面先说说纵向关系的类图。

纵向关系的类关系含有三种类型,分别是继承关系,实现接口关系以及实现抽象类关系,在不同的语言中,其语言的使用规则不一样,但是从类这个抽象意义的概念来说,是没有什么分别的,继承关系指的是子类拥有父类所允许的一些特定的属性和行为,在面向对象语言中,可能很多人会觉得继承很利害,其实在实际开发中,我们却很少见到那么多的继承关系在用的,为什么呢?这里抛开那些经常所说的多重继承带来的问题不说,其实就我的理解,所谓继承能给我们带来的唯一好处是我们可以少写一些代码而已,可是这个少写代码的好处在实际中真正的并不能带来什么效率的提高,因为你想少写代码,势必会在父类写很多东西,而这个却很可能造成巨类的后果,并且有可能违背了一个类只做与它相关事情的原则。所以继承关系在面向对象语言里面虽然很重要,但是实际使用中不必要强行做继承关系。下面为继承关系的类图:

从继承关系我们也可以看出,类与类之间的纵向关系从样子上看起来,就是一个类在另一个类的上面(当然这样说很牵强,但我们在画图时,尽量这样去表现,可以一目了然),而横向关系,则通常是两者具有同等位置的关系,这也表现了一种平等的关系,而纵向关系是不平等的。

对于纵向关系的其他两种类型,接口实现和继承抽象类,由于不同的做图工具,其表现的样式不一样,这个就不好画图了,我的建议是,接口实现和继承抽象类也用上面的一般继承关系的画法,只是需要将这几者区别开来,那么怎么区别呢?有些做图工具的区别是两个类之间的联系图上做手脚,有些图是在父类上加上一些特殊的标识,我觉得最好的办法是就在旁边加上一个文本注释最好,简单明了,一目了然,示例如下:

 

这样让阅读类图的人一看就明白是怎么回事了。可能有些人会有疑问,这样与UML的标准不一样,可是我要说的是,有时需要灵活变通一下,在UML未出现以前,我们不会用这样的标准来做软件设计,可是我们通常会在讨论设计时,画一些图出来,而那些图不是UML标准,但是对于讨论者和阅读者都能明白,而这才是我们的目的,不要拘泥于形式上上的东西,我们学UML,用UML,无非是想让它更好地为我们的设计服务,而不是为了画出漂亮的UML图。

纵向关系的继承抽象类画法同上面所述,这里不再缀述。在做纵向关系时,关键要明白的是哪些要做成基类,哪些要做成接口,而哪些要做成抽象类,这个在UML之外,与具体的业务相关了。

现在说说横向关系。横向关系含有四种类型,分别是关联,依赖,组合,聚合,很多时候,我们并不能很好地区分这四种关系,为什么?因为针对不同的开发语言,其表现形式是不一样的,比如针对依赖关系来说,在C++语言中,通过头文件,函数参数来加以区别,而在JAVA中,头文件的概念没有,而是通过包的形式来表现。还有组合与聚合,通过对对象的生命周期的管理来判定,在象JAVA语言而言,其自动垃圾回收机制,使我们很多时候没有考虑到对象的生命周期结束的问题,所以对组合与聚合就更加模糊了。有一种区别方法是通过业务模型来区别,这种方法比直接与开发语言关联讲解要好些,但是有一个问题是业务领域的分析,由于过于抽象,不一定别人能够完全明白是怎么回事。这使我们想到了如果不采用UML这种工具时,在做设计时,我们仍然能够很好地通过参与人员自行定义的类型进行设计和讲解,从这个角度来说,不采用UML我们一样能做设计,那么如果我们要强行地进行组合与聚合的区别,则太过于注重表现形式,而忽略了我们采用UML这种技术主要是用来进行设计和交流的,只要设计人员和开发人员能够通过图一目了然,这才是最有效的方式。

说到这里,我所想要表达的意思是,我们不必要去区别于组合,聚合到底有什么区别,那只是形式而已,我们更应试关注的是实质性的东西,即我们到底该如何用好UML这个技术,给我们带来更快的设计和交流。这又回到了起点了,即不管他们的表现形式如何,其实都是一种横向关联,有些人把组合与聚合看成是关联关系的一种特殊情况,即含有一对多时的关系为组合或者聚合,而一对一的为关联关系,这个看起来也是没有错的。不过我觉得没有必要这样区别,我们可以认为都是关联关系,至于如果出现一对多的情况,则可以在旁边加上注释说明,则简单明了,也不用去给很多不太明白UML技术的人讲解含有实心棱形的是聚合,含有空心棱形的是组合(讲解起来实在是太累了)。下图为实际使用时的注释,通过在注释中加入说明性的文字,可以很容易明白二者的关系。

 

通过上面的方式,可以很好的回避关联关系与组合,聚合关系等的复杂性,使得设计出来的类图关系清晰,易懂。

现在还有一种关系需要说明,就是依赖关系,在很多人讲解的关系说明中,通过加入头文件,是否是类属性还是函数参数等加以区别,可是由于开发语言的语法不同,表现形式也有所区别,这也仍然犯了只流于表面形式的错误。在实际情况下,我们没有必要去关系到底是关联关系还是依赖关系,因为不管是哪种关系,都会告诉我们一个基本的事情,我们需要其他类的对象来实现本类对象所要做的事情,这个才是最根本的。不管是作为类的成员属性还是函数参数,那只是表面现象而已,我们更应该关注的是,我们依赖于另一个类,是因为要想完成本类所要做的事情,我们需要其他类的协助,这也是我把它们统一称为横向关系的原因所在。

到这里,应该明白类之间的这两种关系的区别了,纵向关系是指的是不依赖于其他类对象,而横向关系则依赖于其他类对象。还需要注意的是,横向关系中,我偏向于将所有的关系描述为依赖关系,而不是关联关系,这个从现实的角度来说,更加合理一些,比如人与人之间发生联系时,通常是某人依赖于另外的人的存在,而简单地描述为关联关系,不够贴切,并且既然发生了联系,则必须是我们需要对方提供协助,这就是依赖于对方的协助。而在有些技术中提到了依赖注入,其中的依赖两个字的意思其实就是指的这里的关系表现,即类与类对象之间的依赖关系。而依赖注入的注入是什么意思,这个我会在后面讲解一下。

好了,最后要说明一点就是,我们在做设计是,对于类与类之间的关系,只要明白了横向与纵向之间的区别即可,在画UML类图时,不必太过于关注UML本身的语法,因为我们画类图的目的是一方面便于我们自己表达,另一方面是让其他设计者或者开发人员更快更容易的明白,复杂的图形只会加重阅读者和交流者的心理负担,而不会带来明显的好处。同时,在画UML类图时,我们是否在设计阶段时,把所有的类都定义好,画出来呢?这个问题不好回答,我举一个我在实际项目中应用的例子,希望能够说明问题。我曾经在做一个复杂系统时,在做类的设计时,当时希望所做的设计足够详细,这样使开发人员能够很快地完成代码的编写,但是在实际的开发中,我发现,代码的编写总不能很好地按照最初的设计进行,主要原因是你的设计中或多或少的有所欠缺,不可能很完美,以及在适应后期的需求变更时,可能也会影响最初的类设计。这样一来,在后期的代码开发中,如果出现和最初的类图有差别,则需要回来维护以前的类图。这里可能带来的一个坏处是由于之前把所有相关的类都考虑到了,如果再修改,可能会有牵一发而动全身的危险。导致谁也不敢去维护类图了。所以我在后面的使用中,只对关键的类关系进行类图的整理与维护,这样在后期维护中,很容易,而对于一些简单的类,只需要在文档中进行说明即可。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值