【设计模式】类间关系
类图
类图是一种软件工程中的**统一建模语言(UML)**的静态结构图,用于描述系统的类集合、类的属性和类之间的关系。它主要用于概念建模,将系统的模型转化为代码。
类图的主要元素包括:
- 类的三个区域:
- 最上面是类的名称。
- 中间部分包含类的属性(字段)。
- 底部部分包含类的操作(方法)。
- 成员可见性:
+
表示公共(public)成员。-
表示私有(private)成员。#
表示保护(protected)成员。~
表示包(package)成员,即对包内其他成员可见。
- 类图可进一步结合状态图或UML状态机来描述系统的行为。
类图是一种强大的工具,通过图形化的方式展示了系统中的类、它们之间的关系以及类的内部结构,帮助我们更好地理解和设计软件系统。
类间关系
类之间的关系指的是在面向对象编程中,不同类之间可能存在的连接和依赖关系。这些关系描述了类之间的交互方式和相互影响。
类之间的关系主要分为以下六种:
-
依赖关系(Dependency):一个类的实现依赖于另一个类的定义,但两者并没有强耦合。一个类的变化不会影响另一个类的实现,只是依赖关系较弱的一种关系。
-
关联关系(Association):两个类之间有一定的关联,表示一个类知道另一个类的存在。关联关系可以是单向的或双向的。关联关系比依赖关系更强,两者之间有更紧密的联系。
-
聚合关系(Aggregation):表示整体与部分之间的关系,是一种强于关联关系的关系。聚合关系中,整体和部分可以分开存在。例如,一个班级包含多个学生,但学生可以存在于其他班级。
-
组合关系(Composition):也表示整体与部分之间的关系,但是组合关系中整体和部分之间具有更强的耦合。整体和部分的生命周期是相互依赖的,部分不能脱离整体而存在。例如,一个汽车包含引擎和轮胎,它们之间是组合关系。
-
泛化关系(Generalization):即继承的反方向,指的是一个类(称为父类、父接口)具有另外的一个(或一些)类(称为子类、子接口)的共有功能。子类可视为其父类的特例,并可以增加新功能。
-
实现关系(Realization):表示类与接口之间的关系,一个类实现了一个接口,必须实现接口中定义的所有方法。
依赖
依赖关系是一种比较松散的关系,表示一个类依赖于另一个类的定义,但两者之间的耦合度相对较低。在依赖关系中,一个类的变化不会直接影响到另一个类的实现。
你可以这么理解,一个类只要用(using)到了另一个类,那么它们之间就存在依赖关系。依赖关系仅仅描述了类与类之间的一种使用与被使用的关系。
比如现在有一个Driver
类和一个Car
类,Driver
类依赖于Car
类来实现其功能:
1 | class Car { // 汽车类 |
可以看到,Car
(汽车)和Driver
(驾驶员)之间存在依赖关系。
Driver
类有一个drive
方法,该方法的参数类型是Car
,表明驾驶员依赖于汽车对象。在主类DriverAndCar
的main
方法中,创建了一个Driver
对象并调用其drive
方法,传入一个Car
对象(奥迪),从而实现了驾驶员驾驶汽车的过程。
在UML
类图中,依赖关系用带箭头的虚线表示,由依赖的一方指向被依赖的一方。
依赖关系的存在使得类与类之间的耦合度相对较低,有利于代码的灵活性和可维护性。
关联
关联关系,表示一类对象与另一类对象之间有联系的一种结构化关系。它反映了整体与部分之间的关系,通常表现为一个类(或接口)类型的对象作为另一个类的属性(字段)。
关联关系可以分为单向关联和双向关联。
-
单向关联: 一个类的对象作为另一个类的属性,表示一种单向的拥有关系。例如,Customer与Address的关系,表示顾客拥有快递地址。
-
双向关联: 两个类的对象互为属性,表示双向的关系。例如,Employee与Department的关系,表示员工与部门之间存在双向关联。
关联关系可细分为聚合和组合两种使用方式:
-
聚合(Aggregation): 部分类对象可以独立存在于整体类对象之外,关系较为松散。例如,Company与Employee的关系,表示公司包含员工,但员工可以独立存在。
-
组合(Composition): 部分类对象与整体类对象具有统一的生存期,整体类对象的生命周期控制部分类对象。例如,Head与Mouth的关系,表示头部包含口,口的生命周期与头的生命周期相关联。
此外,关联关系中还可能存在自关联,即一个类的属性对象类型为该类本身,例如单链表中的结点类。
在UML类图中,关联关系用实线连接有关联的两个类。单向关联用一个带箭头的实线表示,箭头从使用类指向被关联的类,双向关联用带箭头或者没有箭头的实线来表示。
聚合
聚合(Aggregation)是一种表示整体与部分的一类特殊的关联关系,可以理解为一种“… owns a …”(拥有)关系。
聚合关系是一种弱关联,部分对象可以独立存在,不依赖于整体对象。它们可以具有各自的生命周期。比如图书馆包含(owns a) 学生和书籍(即使没有图书馆,学生也可以存在)。再比如老师和课程之间的关系也是聚合。一个教授可以教授多门课程,而每门课程又可以由不同的教授来教,并且各自的生命周期可以独立存在。
在代码实现时,类对象通过构造器或setter
方法进行注入。
举个例子:
1 | class Employee { // 部分类 |
在这个例子中:
-
Company类(整体类): 代表了整体,即公司。它有一个属性是
employees
,用于存储雇佣的员工列表。 -
Employee类(部分类): 代表了部分,即员工。员工有一个属性
name
表示姓名。
通过这个设计,公司(整体类)可以包含多个员工(部分类),而员工可以独立存在,不依赖于任何特定的公司(公司关了,员工也不会消失)。这符合聚合关系的特性,即部分类对象可以脱离整体类对象而独立存在。
类图如下:
聚合关系表示为一条实线,在关联端带有一个未填充的菱形,该实线连接到表示聚合的类。
组合
组合(Composition)关系是一类“强”的整体与部分的包含关系(" … is a part of …"),部分类对象与整体类对象具有统一的生存期。当整体类对象消亡时,部分类对象也将消亡。或者说,整体类对象控制了部分类对象的生命周期。
整体对象完全负责创建、销毁和管理部分类对象。部分类对象无法独立存在或被其他对象所拥有。在代码实现时,部分类对象在整体类属性声明时或它的构造方法里实例化。
举例:
公司与公司部门之间的关系可以被视为组合关系。一个公司包含多个部门,而且公司的存在决定了部门的存在,整体对象(公司)管理部分对象(部门),整体对象的销毁将导致部分对象的销毁。
1 | import java.util.ArrayList; |
类图如下:
在类图中,组合关系通常用实心的菱形箭尾和实线表示,箭头指向整体对象。
泛化
泛化(Generalization)关系指的是一个类(称为父类、父接口)具有另外的一个(或一些)类(称为子类、子接口)的共有功能。实际上就是类(或接口)之间的继承关系。
子类继承了父类的属性和方法,可以视为是父类的特例,同时具有扩展或增加新功能的能力。通过泛化,可以将通用的行为和属性提取到父类中,从而提高代码的复用性和可维护性。
举例:
1 | // 父类 |
在这个例子中,Dog
和Cat
类继承自Animal
类,形成了泛化关系。Animal
类包含了共有的eat
方法,而Dog
和Cat
类分别增加了独有的功能。
在类图中,泛化关系通常用带空心三角形的箭头和实线表示。箭头指向父类,表示子类继承自父类。
实现
实现(Realization)关系很简单,就是指一个class
类实现interface
接口(可以是多个)。实现类需要实现接口中声明的所有抽象方法。
在Java中此类关系通过关键字implements
明确标识。
举例:
1 | interface Vehicle {//定义接口 |
在类图中,实现关系用带空心三角形箭头的虚线表示。