uml 类图学习
uml 类图学习
前言
最近在想写一些代码文档的时候发现这方面的知识有所欠缺,没有相关的工具确实没办法梳理某些工程项目的脉络
遂针对 uml 及其在 markdown 中的使用记录一些笔记
简介
uml 全称是 unified modeling language 这是包含各类图表的标准化语言,主要用于帮助软件工程师识别,构建某些软件系统
他的主要识别对象为 面向对象语言 通过大量的图形描述软件系统的设计, 其主要可以又如下分类
- 类图
- 组件图
等等
具体分类参考what is uml
针对上述分类,将详细讲讲上述图的详情
类图
首先,类图是 静态结构图表 的一种,主要用于描述软件系统中的类/成员/方法以及其与其他类的关系
类图的图形包含以下要素
- 描述类的图形集合
- 描述类与类之间关系的集合
类的图形集合
首先类的图形包含以下要素
- 类名
- 类的成员
- 类的方法
一个基本的类如下图所示
MyClass 这个类有三个成员及三个方法,其中
op1方法返回的是string类型,op3返回的是Class6类型+表示为公共成员或方法,-表示为私有成员或方法,#表示为保护成员及方法,~表示为包里的成员及方法
类之间的关系
前言:类与类之间的关系有如下几大类
-
依赖关系 (Dependency)
Use-A- 一个类使用另一个类,但不持有其引用
-
关联关系 (Association)
Has-A-
一个类持有另一个类的引用
-
包括以下特殊形式:
a. 聚合关系 (Aggregation)
- 整体与部分的关系,部分可以独立存在
b. 组合关系 (Composition)
- 更强的整体与部分的关系,部分不能独立存在
-
-
继承关系 (Inheritance)
Is-A- 一个类是另一个类的子类
-
实现关系 (Realization)
- 一个类实现一个接口
接下来,针对上文三种基本关系进行讨论
依赖关系
依赖关系代表两个类是 Use-A 关系,简称 A use a B 也就是 A使用了B 代码如下所示
class Car {
public void carStartDemo(){
Engine audiEngine = new Engine();
audiEngine.start();
audiEngine.stop();
}
}
class Engine {
public void start(){
System.out.println("start your engine");
}
public void stop(){
System.out.println("stop your engine");
}
}
其 uml 图如下所示
其核心为, Car 类的 CarStartDemo() 方法只是使用了 Engine 类,Engine 类并没有作为 Car 的成员存在,所以两类的关系为 依赖关系
除了上述依赖形式(局部变量依赖),依赖关系还可以通过其他方式体现,如
- 方法参数依赖:一个类的方法接受另一个类的对象作为参数
- 返回型依赖:一个类的方法返回另一个类的对象
- 局部变量依赖:在方法中创建另一个类的对象作为局部变量
- 静态方法调用:调用另一个类的静态方法
其代码如下所示
class Car{
//方法参数依赖
public void StartEngine(Engine engine){
engine.Start();
}
//返回型依赖
public Engine createEngine(){
return new Engine();
}
//局部变量依赖
public void carStartDemo(){
Engine audiEngine = new Engine();
audiEngine.start();
}
//静态方法调用
public void checkEngineStatus(){
boolean is Running = Engine.isRunning();
}
}
class Engine{
public void Start(){
System.out.println("start your engine");
}
public static isRunning(){
return true;
}
}
注意:依赖关系 代表两个类之间的 生命周期 并不严格互相依赖
- 方法参数依赖的生命周期主要看调用者的使用
- 返回型的生命周期主要看调用者使用
- 局部变量的生命周期主要看方法执行期间
- 静态方法调用的生命周期:(压根没有创建实例,没有生命周期可言)
关联关系
关联关系代表两个类是 Has-A 关系,简称 A has a B 代码如下所示
import java.util.ArrayList;
import java.util.LInkedList;
import java.util.List;
public enum VehicleType{
TRUCK,
BICYCLE,
SUV
}
// 使用泛型
// 使用重载
public class Car<T extends List<Product>>{
private final VehicleType type;
private final Engine engine;
private final T products;
public Car(VehicleType type){
this(type, (T) new ArrayList<>());// 针对 ArrayList 类型做了类型转换
}
public Car(VehicleType type,T products){
this.type = type;
this.engine = new Engine();
this.products = products;
}
public void carStartDemo{
this.engine.Start();
}
}
public class Engine{
public void Start(){
System.out.println("start your engine");
}
}
public record Product(String name,int weight){
@Override
public String toString(){
return String.format("Product: name is %s,weight is %d",name,weight);
}
public void printDetails(){
System.out.printf("Product Details: name os %s, weight is %d kg%n",name,weight);
}
};
其 uml 图如下图所示
上述代码覆盖了关联,聚合及组合关系
- 关联关系 (association),指代
Car与VehicleType的关系,在上述代码中,VehicleType实际上是一个预定义常量,其生命周期并不由Car类掌握VehicleType的生命周期,所以说Car has a VehicleType - 聚合关系 (aggregation),指代
Car与Product的关系,在上述代码中,Product以List的形式作为Car类型的成员变量,其中Car并不掌握Product的生命周期,所以说Car has many Product - 组合关系 (composition),指代
Car与Engine的关系,在上述代码中,Engine的实例由Car创建,Engine的生命周期依赖于Car,所以说Car contains Engine
以上三者的关系如下图所示

更详细的代码,解释可以参考 association-composition-aggregation-java 这篇文章
而在 uml 图中,三者可以这样表示
| Type | Description |
|---|---|
--> |
association |
--o |
aggregation |
--* |
composition |
继承关系
继承关系代表两个类是 Is-A 关系, 简称 A is a B 也就是 B是A的父类 代码如下所示
public enum DriveMode {
MANPOWER,
GASOLINE,
NATURAL_GAS,
}
public abstract class Vehicle{
private final int horsePower;
private final int carryingCapacity;
public Vehicle(int horsePower,int carryingCapacity){
this.horsePower = horsePower;
this.carryingCapacity = carryingCapacity;
}
public integer getHorsePower(){return this.horsePower};
public integer getCarryingCapacity(){return this.carryingCapacity};
@Override
public String toString(){
return String.format("the vehicle's horsePower is %d,carrying capacity is %d",this.horsePower,this.carryingCapacity);
}
}
// 新建另外一个抽象类,用于做 Car 类 以及 Bike 类的高级抽象( Car 与 Bike 两个类的构造方法很类似 )
public abstract class BrandedVehicle extends Vehicle{
private final String brandName;
private final DriveMode driveMode;
public BrandedVehicle(int horsePoser,int carryingCapacity,String brandName,DriveMode driveMode){
super(horsePower,carryingCapacity); // 调用 Vehicle 的构造函数
this.brandName = brandName;
this.driveMode = driveMode;
}
public String getBrandName(){return this.brandName};
public DriveMode getDriveMode(){return this.driveMode};
@Override
public String toString(){
return String.format("%s: brandName=%s, driveMode=%s, %s",
this.getClass().getSimpleName(), this.brandName, this.driveMode, super.toString());
}
// this.getClass().getSimpleName() 这个函数的作用在于返回当前对象的雷鸣
// this.getClass() 这个函数获取当前对象的运行时类
// .getSimpleName() 返回该类的简单名称
// 使用这个函数就可以避免在 Car 类以及 Bike 类中重新定义 toString 方法了,无需硬编码了
}
public class Car extends BrandedVehicle {
// 该函数调用同类的四参数构造函数,也就是下面的那个构造函数,简化写法,这种写法叫 *构造函数链接* (constructor chaining)
public Car(int horsePower, int carryingCapacity, String brandName) {
this(horsePower, carryingCapacity, brandName, DriveMode.GASOLINE);
}
public Car(int horsePower, int carryingCapacity, String brandName, DriveMode driveMode) {
// 调用父类构造函数
super(horsePower, carryingCapacity, brandName, driveMode);
}
}
public class Bike extends BrandedVehicle {
public Bike(int horsePower, int carryingCapacity, String brandName) {
this(horsePower, carryingCapacity, brandName, DriveMode.MANPOWER);
}
public Bike(int horsePower, int carryingCapacity, String brandName, DriveMode driveMode) {
super(horsePower, carryingCapacity, brandName, driveMode);
}
}
public class DiyVehicle extends Vehicle{
private final DriveMode driveMode;
private final String author;
public DiyVehicle(int horsePower, int carryingCapacity, DriveMode driveMode, String author){
super(horsePoser, carryingCapacity);
this.DriveMode = driveMode;
this.author = author;
}
@Override
public String toString(){
return String.format("%s: author=%s, driveMode=%s, %s",
this.getClass().getSimpleName(), this.author, this.driveMode, super.toString());
}
}
其 uml 图如下所示
在上述代码中,共涉及到 Vehicle BrandedVehicle DiyVehicle Car Bike DriveMode 这6个类
可以看到 Vehicle BrandedVehicle DiyVehicle Car Bike 这几个类互相为继承关系,具体关系还是看上图
同时,我们可以说 BrandedVehicle is a Vehicle, Car is a BrandedVehicle
BrandedVehicle Car Bike 与 DriveMode 类则是关联关系
实现关系
前文已经提出,实现关系是指 一个类实现了另一个接口 下文是示例代码
interface Vehicle{
// 隐式 public void
void start();
// 隐式 public void
void stop();
// 隐式 public String
String getDescription();
// 默认方法,并不需要实现
// 隐式 public
default void honk(){
System.out.println("be be");
}
// 静态方法,并不需要实现
static boolean isVehicle(Object obj){
return obj instanceof Vehicle;
}
default void performSafetyCheck(){
if(checkEngine() && checkBrakes()){
System.out.println("Vehicle is safe to drive");
}else{
System.out.println("Vehicle needs maintenance");
}
}
// 私有方法,实现类无法直接访问,只能间接通过 default 方法 *performSafetyCheck* 进行调用访问
private boolean checkEngine(){
// ... 一些比较复杂的检查逻辑
System.out.println("Checking engine");
return true;
}
// 接口里的私有方法与类中的私有方法较为类似
private boolean checkBrakes(){
// ... 一些比较复杂的检查逻辑
System.out.println("Checking brakes");
return true;
}
}
Class Car implements Vehicle{
private String band;
public Car(String brand){
this.brand = brand;
}
@Override
public void start(){
System.out.println(brand + " car is starting");
}
@Override
public void stop(){
System.out.println(brand + " car has stop");
}
@Override
public String getDescription(){
return "This is " + brand + " 's car";
}
}
其 uml 类图如下所示
在上述代码中,我们可以说 Car 类实现了 Vehicle 接口
Q:什么时候该用 继承关系 什么时候该用 关联关系呢
A:主要还是看是打算共用哪些内容. 如果说共用的是一些行为,那么用接口比较合适。而如果共用的是父类的大多数功能,那么就还是要使用继承关系,也就是当需要共享状态的时候就使用继承
总结
上文总共从 类的定义 及 类与类之间的关系 两个大方面展示了 uml 图该如何画
有一些比较重要的点,需要关注
- 依赖关系与关联关系的区别(生命周期)
- 关联关系三兄弟之间的区别
- 什么时候该用继承关系,什么时候该用实现关系(代码具体实践)
组件图
前言
组件图通过抽象系统中的功能组件,描述大型软件工程中的各组件(功能)之间的关系。达到可视化记录工程结构的目的,其本质是类图,但是区别在于抽象的层级不同,组件图是系统架构层面的图表,类图则关心功能类的具体实现
具体内容
让我们举一个与汽车有关的例子
- 涉及对象方面
- 汽车本身
- 操控组件
- 动力组件
- 能源组件
- 用户(外部actor)
- 外部能源(外部系统)
- 接口方面
- 操控组件 对外提供端口(方向盘)
- 动力组件 对外提供端口(油门,刹车)
- 能源组件 对外提供端口 能源补充端口
- 能源组件 对内提供接口 燃料供应接口
- 关系方面
- 用户 通过端口(方向盘)使用 操控组件 提供的服务
- 用户 通过端口(油门,刹车)使用 动力组件 提供的服务
- 动力组件 通过接口(燃料供应端口)使用 能源组件 提供的服务
- 能源组件 通过端口(燃料供应接口)使用 外部能源 提供的服务
其 uml 图如下图所示
上述图中有一些核心要素,如下图所示
总结
本篇文章详细介绍了如何画 类图 及 组件图
还有一些比较重要的如 用例图 序列图 下一篇章节再讲

浙公网安备 33010602011771号