从零开始学习Java设计模式 | 设计模式入门篇:UML图
Posted 李阿昀
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从零开始学习Java设计模式 | 设计模式入门篇:UML图相关的知识,希望对你有一定的参考价值。
在上一讲中,我为大家介绍完了第一章中的第一部分内容,接下来,在本讲中,我便来为大家介绍一下第一章中的第二部分内容,即UML图。
那么,UML它到底是什么呢?它被称为统一建模语言(Unified Modeling Language,简称为UML),即用来设计软件的可视化建模语言。其特点是简单、统一、图形化,能表达软件设计中的动态和静态信息。而在本讲中我们所要学习的UML里面的类图,它主要体现的就是静态信息。
下面,我们来看一下UML它包含了哪些图?UML从目标系统的不同角度出发,定义了用例图、类图、对象图、状态图、活动图、时序图、协作图、构建图、部署图等9种图。而我也在上面说过了,在本讲中,我们所要学习的是UML里面的类图。
类图的概述
什么是类图呢?
类图(Class diagram)是显示了模型的静态结构,特别是模型中存在的类、类的内部结构以及它们与其他类的关系等。类图不显示暂时性的信息。类图是面向对象建模的主要组成部分。
上面这句话中提到了类的内部结构,想必大家也很清楚这其实说的就是一个类里面到底有哪些成员?你不会不知道一个类里面到底有哪些成员吧!无非就是成员变量、构造方法以及成员方法,相信你一定知道这些。
其次,上面这句话中还提到了一个关系,该关系是特别重要的,而且类图也定义了一些规则来进行描述。
类图的作用
类图它到底有什么作用呢?我们不妨来看一下。
-
在软件工程中,类图是一种静态的结构图(也就是说一个类中有哪些成员,以及该类和其他类到底有什么关系,这都是静态的,并且实实在在存在的),描述了系统的类的集合,类的属性和类之间的关系,可以简化人们对系统的理解
-
类图是系统分析和设计阶段的重要产物(正好,设计模式就在设计阶段来研究的),是系统编码和测试的重要模型
类图的作用说白了,其实就是以图形化的形式让我们更好地去理解整个系统里面包含了哪些类、类里面的结构以及类和类之间的一个关系。
类图表示法
了解了类图之后,接下来我们来看下类图表示法。在类图表示法这一章节,我们所要学习的内容有:
- 类的表示方式,当然,也包括含接口的表示方式
- 类和类之间关系的表示方式,当然,也包含类和接口之间关系的表示方式
接下来,我们先来看一下类的表示方式。
类的表示方式
在UML类图中,类使用包含类名、属性(field,也即成员变量)和方法(method)且带有分割线的矩形来表示,比如下图就表示了一个Employee类,它包含name、age和address这3个属性,以及work方法。
看清楚上面这张图没有,它其实就是一个矩形,而且该矩形被分成了三部分,第一部分表示的是类名,第二部分是成员变量,第三部分是成员方法。但是,这里面有一个问题,就是这里面的减号、加号以及冒号分别表示的是什么含义呢?要搞清楚这个,那我们就得知道类图中成员变量和和成员方法的表示方式。
属性/方法名称前加的加号和减号表示了这个属性/方法的可见性,也就是我们之前学习的访问修饰符,我们知道Java中访问修饰符有4种,那么UML类图中表示可见性的符号有几种呢?可能会出乎你的意料,其有3种,分别是:
+
:表示public-
:表示private#
:表示protected
有些同学可能会想,不是应该有4种吗?为什么只有3种啊?其实,还有一种是默认,也就是什么都不加,在类图中表示的话,什么都不加就可以了。
接下来,我们来看一下属性和方法的完整表示方式。
- 属性的完整表示方式:
可见性 名称 : 类型[ = 缺省值]
- 方法的完整表示方式:
可见性 名称(参数列表)[ : 返回类型]
注意:
- 中括号中的内容表示是可选的
- 也有将类型放在变量名前面,返回值类型放在方法名前面的,但是还是建议大家按照以上标准方式来写
明了上面的知识点之后,我们再来重新分析一下Employee类的类图,可以很清楚地看到该类里面有3个成员变量,而且它们前面加的都是-
,这说明它们都是用private修饰的;其次,我们还能看到,name成员变量的类型是字符串,age成员变量的类型是int,address成员变量的类型也是字符串;最后,我们也能看到,该类里面只有一个成员方法,而且它是public修饰的,没有参数,也没有返回值。
这个时候,相信你再来看某个类的类图,一定会很好理解了。
不妨我们再来看一个Demo类的类图,如下图所示。
可以看到,Demo类里面没有成员变量,只有3个成员方法,我们不妨来分析一下每一个方法:
- method方法:修饰符为public,没有参数,没有返回值
- method1方法:修饰符为private,没有参数,返回值类型为String
- method2方法:修饰符为protected,接收两个参数,第一个参数类型为int,第二个参数的类型为String,返回值类型是int
类与类之间关系的表示方式
在这一章节,我们来探讨一下类和类之间关系的表示方式,当然了,这里面也包含类和接口之间关系的表示方式。那么类和类之间都有哪些关系呢?嘿嘿!关系还挺多的,一共有如下这些。
- 关联关系
- 聚合关系
- 组合关系
- 依赖关系
- 继承关系
- 实现关系
这些关系中,继承关系和实现关系,我们还是比较熟的。至于其他的关系,你不熟,也没关系,因为下面我会为大家详细地阐述每一种关系。不妨,我们就先来看一下关联关系。
关联关系
什么是关联关系呢?
关联关系是对象之间的一种引用关系,用于表示一类对象与另一类对象之间的联系,如老师和学生(一个老师可以教多个学生)、师傅和徒弟(一个师傅可以教多个徒弟)、丈夫和妻子(当然,这种必须是一对一的关系哟😊)等。关联关系是类和类之间最常用的一种关系,可分为一般关联关系、聚合关系和组合关系。
如何去理解以上这句话中的引用关系呢?我们可以这样理解,所谓的引用就是可以在一个类中声明另外一个类型的变量。
从以上关联关系的概述中,我相信大家也能知道这点,即聚合关系和组合关系它们也属于关联关系,只不过这里我是单独的把它们拿出来讲解而已!
下面,我来为大家讲一下关联关系所属里面的一般关联关系,而且大家要知道一般关联关系又可分为单向关联、双向关联以及自关联哟!那没办法了,只好分类来介绍了。
单向关联
请看一下下面这张类图,有一个顾客类和地址类,并且还能看到在顾客类的成员位置声明了一个地址类型的变量,相信大家已经猜到了,这表示的就是引用,即引用了Address类的对象,很显然,这个就属于单向关联。
那么,单向关联在类图中是如何来表示的呢?在UML类图中,单向关联是用一个带箭头的实线来表示的,也就是说,这条实线表示的其实就是单向关联关系。上图表示的就是每个顾客都有一个地址,这通过让Customer类持有一个类型为Address的成员变量来实现。
双向关联
请看一下下面这张类图,该类图里面有两个类,一个是顾客类,还有一个是商品类。请注意,由于一个顾客可以购买多个商品,所以在顾客类的成员位置处声明了商品类型的变量,只不过它是一个集合;而商品类中也声明了一个顾客类型的变量,表示该商品属于某一个顾客,很显然,这个就属于双向关联。
那么,双向关联在类图中是如何来表示的呢?在UML类图中,双向关联是用一个不带箭头的直线来表示的。上图中在Customer类中维护了一个List<Product>
集合,且该集合里面存的是商品对象,这表示一个顾客可以购买多个商品;在Product类中维护了一个Customer类型的成员变量,表示这个产品被哪个顾客所购买了。相信大家从上图中也能很容易看出,所谓的双向关联就是双方各自持有对方类型的成员变量。
自关联
自关联比较特殊,相对而言用的其实也并不是特别多。我们不妨看一下下面这张类图,有一个Node类,而且Node类里面又声明了一个Node类型的变量(也即成员变量),很显然,这个就属于自关联。
那么,自关联在类图中是如何来表示的呢?自关联在UML类图中是用一个带有箭头且指向自身的线来表示的。上图的意思就是Node类包含类型为Node的成员变量,也就是"自己包含自己"。
还记得我们之前学习过的一个叫LinkedList的集合吗?它底层其实就用到了这种自关联的结构。
聚合关系
什么是聚合关系呢?聚合关系,上面我也已经说过了,它和组合关系都是属于关联关系里面的,也即关联关系的一种,而且它是强关联关系,是整体和部分之间的关系。
什么叫整体和部分之间的关系呢?就拿大学和老师这个例子来说,这里我为了让大家更好地理解,我直接给出了大学和教师的关系图,如下图所示,以该例子而言,大学属于整体,老师就属于部分。
此外,关于聚合关系,我们还得知道一点,那就是聚合关系也是通过成员对象来实现的,其中成员对象是整体对象的一部分,但是成员对象可以脱离整体对象而独立存在。
这句话怎么理解呢?从上图中可以看到,在大学里面,是有成员的,而且成员还是另外一种类型(即老师)的对象,只不过它是放在了一个集合里面,所以我们才说聚合关系是通过成员对象来实现的;其次,由于大学是一个整体,而老师是部分,也即成员对象,所以很显然成员对象是整体对象的一部分;最后,一个大学肯定会有多位老师,如果这个大学停办了,或者说这个大学没落了,那么老师是还依旧存在的,他还可以到其他的学校继续任职,所以我们才说成员对象可以脱离整体对象而独立存在。
那么,聚合关系在类图中是如何来表示的呢?在UML类图中,聚合关系可以用带空心菱形的实线来表示,而且菱形指向整体。在上图中,老师是属于部分,大学是属于整体,所以空心菱形就得指向于大学这一边,这点大家也能很清晰地看出来。
组合关系
这一小节,我们再来看一下关联关系的另外一种,即组合关系。那什么是组合关系呢?
组合表示类之间的整体与部分的关系,但它是一种更强烈的聚合关系。
从以上这句话中我们可以知道,组合关系也是整体和部分的关系,既然如此,那么它和上面讲到的聚合关系到底又有什么区别呢?区别就是组合关系是一种更强烈的聚合关系,那怎么体现更强烈这一点呢?请看下面这句话。
在组合关系中,整体对象可以控制部分对象的生命周期,一旦整体对象不存在,部分对象也将不存在,部分对象不能脱离整体对象而存在。例如,头和嘴的关系,头属于整体,嘴属于部分,没有了头,嘴也就不存在了,因为嘴是依赖于头而存在的。
所以,关于组合关系的概念,大家一定要清楚哟,而且也要知道它和聚合关系不太一样的地方。
那么,组合关系在类图中是如何来表示的呢?在UML类图中,组合关系用带实心菱形的实线来表示,而且菱形指向整体。
这与上面所讲到的聚合关系还是有所区别的,虽然都有讲菱形指向整体,但是一个是实心菱形,一个是空心菱形,所以,我们就可以通过判断菱形是空心的还是实心的,来区分开其到底是聚合关系还是组合关系。
当然了,组合关系在我们的日常生活中还是用的比较多的,可能我们在真正定义类的时候,相对而言用的并不是特别多。在现实生活中,就拿我上面举的头和嘴的例子来说,其关系图就应该是下面这样子的。
大家一定得注意了,对于组合关系而言,如果整体不存在了,那么部分肯定不复存在了。
依赖关系
什么是依赖关系呢?
依赖关系是一种使用关系(在我们平时定义类的时候特别常用),它是对象之间耦合度最弱的一种关联关系,是临时性的关联。在代码中,某个类的方法通过局部变量、方法的参数或者对静态方法的调用来访问另外一个类(被依赖类)中的某些方法来完成一些职责。
以上这句话中说到了一个词,就是临时性的关联,那什么是临时性的关联呢?举个例子,我们在一个类的方法里面去创建了另外一个类的对象,那么这就是属于依赖关系了,也可说成是临时性的关联,其实,这一点在以上这句话的后半句中也提到了。
请看一下下面这幅司机和汽车的关系图,有一个司机类和汽车类,由于司机要驾驶汽车,所以你能看到在Driver类中有一个Car类型的形式参数,当然了,你也可以说成是Car类在Driver类中充当的是方法的参数,这个就是所谓的依赖关系,也即Driver类依赖于Car类。
当然了,我们还可以在Driver类的drive方法中通过局部变量的形式来描述依赖关系,只不过这儿是以方法的参数这种形式来描述的,至于对静态方法调用也是同样的一个道理,这里我就不再赘述了。
那么,依赖关系在类图中是如何来表示的呢?在UML类图中,依赖关系使用带箭头的虚线来表示,箭头从使用类指向被依赖的类。
这里我再次重复地来分析一下以上司机和汽车的关系图,可以看到,在Driver类里面有一个叫driver的方法,而且方法中使用了一个Car类型的变量,也即Car类型的形式参数,那么到时候在调用driver方法的时候,必然是要给其传递一个Car类型的对象的,这样,我们就可以说Driver类依赖于Car类,这个就是依赖关系。
最后,我再强调一点,依赖关系在真正的开发中用的是最多的,而且它的耦合性也是最弱的。
继承关系
依赖关系讲解完之后,我们再来看一下继承关系和实现关系,它俩在真正的开发中也是用的特别多。不过这一小节,我们先来说一下继承关系。
什么是继承关系呢?
继承关系是对象之间耦合度最大的一种关系,表示一般与特殊的关系,是父类与子类之间的关系,是一种继承关系。
有些同学可能会想,为什么说继承关系是对象之间耦合度最大的一种关系呢?你想啊,当两个类有了继承关系之后,如果我们在父类中定义了一个成员,不管是成员变量还是成员方法,那么子类都会继承过来,所以耦合度就特别高。
在UML类图中,泛化关系也被称为继承关系(也可以说泛化关系表示的其实就是继承关系),只不过这一块我没有单独去说它。那么,泛化关系在类图中是如何来表示的呢?泛化关系在UML类图中是用带空心三角箭头的实线来表示的,而且箭头从子类指向父类。在代码实现时,使用面向对象的继承机制来实现泛化关系(或者说继承关系),这个想必大家应该很熟了。例如,Student类和Teacher类都是Person类的子类,其类图如下图所示。
大家从以上类图中看清楚没有?就是箭头是从子类指向父类的。
总之,只要看到带空心三角箭头的实线,你就应该得明白这表示的就是继承关系。
实现关系
什么是实现关系呢?
实现关系是接口与实现类之间的关系。在这种关系中,类实现了接口,类中的操作实现了接口中所声明的所有的抽象操作。
实现关系的概念很好理解,无非就是我们平常所说所做的,如果实现类要去实现某个接口的话,那么必须就得重写该接口里面的所有的抽象方法了,当然,前提是该实现类不是抽象的类。
那么,实现关系在类图中是如何来表示的呢?在UML类图中,实现关系使用带空心三角箭头的虚线来表示,而且箭头从实现类指向接口。例如,汽车和船实现了交通工具,其类图就如下图所示。
不知你看到了没有,在Vehicle上面有一个<<Interface>>
这样的标识,这表明了Vehicle是一个接口,而且,该接口又有两个子实现类,只因它们上面没有声明<<Interface>>
。
至此,类和类、类和接口之间的关系的表示方式我就彻底讲解完了。不妨我们在脑海中再回顾一下以上所学的知识,想必你就应该会很清楚,关系总共可分为关联关系、聚合关系、组合关系、依赖关系、继承关系以及实现关系,其中又以依赖关系的耦合度最低,以继承关系和实现关系的耦合度最高,当然,在继承和实现这俩关系中,耦合度最高的还属继承关系。
以上是关于从零开始学习Java设计模式 | 设计模式入门篇:UML图的主要内容,如果未能解决你的问题,请参考以下文章
从零开始学习Java设计模式 | 设计模式入门篇:设计模式概述