UML类图怎么画?——图文详细理解

363 阅读6分钟

UML类图是干什么的?UML类图怎么画?为什么别人画的类图清晰明了? UML类图,UML中的一种重要图形,是在面向对象语言用中用来表示一个类。它由两部分组成,类,类之间的关系,可以由下图所示:

ps:聚合关系中,应该是“大雁”聚合于”雁群“,即”大雁“——◇“雁群”

在这里插入图片描述

一、什么是UML类图

UML类图(Class Diagram)是统一建模语言(Unified Modeling Language,UML)中最常用的一种静态结构图,用于描述软件系统的类、接口、属性、方法以及它们之间的关系。它是面向对象设计(OOD)的核心工具,帮助开发者可视化系统的结构,明确类之间的协作方式,并为代码实现提供蓝图。

  • 核心作用

    • 可视化系统结构:通过图形化方式展示类及其关系,便于团队沟通。
    • 指导代码实现:直接对应到编程语言中的类、接口、继承等结构。
    • 分析需求:在需求阶段帮助识别关键类及其职责。
    • 文档化设计:作为系统设计的官方文档,便于维护和扩展。
  • 关系描述

    关系类型符号描述示例
    继承实线+空心三角箭头子类继承父类的属性和方法ElectricCar —▷ Car
    实现(接口)虚线+空心三角箭头类实现接口中的方法ElectricCar --▷ Drivable
    关联实线+箭头(可选)类之间存在使用关系(双向或单向)Driver —> Car
    聚合空心菱形+实线整体与部分的弱关联(部分可独立存在)Car ◇— Engine
    组合实心菱形+实线整体与部分的强关联(部分依赖整体)Car ◆— Wheel
    依赖虚线+箭头类之间临时或偶然的使用关系Driver --> Car

二、类图详解

2.1 类

类是具有相似结构、行为和关系的一组对象的描述符,是面向对象系统中最重要的构造块。 如下图所示,就表示一个类:

在这里插入图片描述

三个格子从上至下分别表示:

  • 类名称(如果是接口,就使用斜体表示)
  • 类的特性(一般是类的字段和属性,可以没有)
  • 类的操作(一般是类的方法或行为)

它们前边的符号有以下几类:

  • “+”表示public
  • “-”表示private
  • “#”表示protected

2.2 类的关系

除了类,类图中还有一个重要元素,即类之间的关系。 根据类的关系的不同,具体可分为6种,分别如下:

2.2.1 继承、泛化关系(用带空心三角形的实线表示)

继承(泛化)关系,它指定了子类如何特化父类的所有特征和行为。例如:鸟是动物的一种,企鹅、鸭、大雁是鸟的一种。

在这里插入图片描述

2.2.2 实现(接口)关系(用带空心三角形的虚线表示)

一种类与接口的关系,表示类是接口所有特征和行为的实现。它有两种表示方法:

  • 第一种,矩形表示法

    • 顶端有<<interface>>
    • 第一行:接口名称
    • 第二行:接口方法 在这里插入图片描述
  • 第二种,棒棒糖表示法

    • 圆圈旁为接口名称
    • 接口方法在实现类中出现 在这里插入图片描述

2.2.3 关联关系(用实箭线表示)

所谓关联关系,就是这个类有一个属性是其他类。 在这里插入图片描述

2.2.4 聚合关系(用带空心菱形的实线表示)

聚合关系是关联关系的一种,是强的关联关系; 特点: 部分对象的生命周期并不由整体对象来管理。也就是说,当整体对象已经不存在的时候,部分的对象还是可能继续存在的。比如:一只大雁脱离了雁群,依然是可以继续存活的。 在这里插入图片描述

2.2.5 组合关系(用带实心菱形的实线表示)

组合关系同样是关联关系的一种,是比聚合关系还要强的关系。 特点:在组合中,部分与整体生命期一致,部分与组合同时创建并同时消亡 。比如:鸟与翅膀的关系。 在这里插入图片描述

2.2.6 依赖关系(用虚箭线表示)

所谓依赖关系,就是构造这个类的时候,需要依赖其他的类,比如:动物依赖水和氧气。如下图所示: 在这里插入图片描述

三、举个栗子

3.1 基础类 & 接口定义

// 依赖对象:氧气(无具体逻辑,仅体现依赖关系)
data class Oxygen(
    val amount: Int = 21 // 默认氧气含量为 21%
)
 
// 依赖对象:水(无具体逻辑,仅体现依赖关系)
data class Water(
    val amount: Int = 1000 // 默认水量为 1000ml
)
 
// 关联对象:气候(无具体逻辑,仅体现关联关系)
class Climate(
    val weather: String = "天晴" // 默认天气类型为天晴
)
 
// 组合对象:翅膀(无具体逻辑,仅体现组合关系)
class Wing
 
// 接口:飞翔(大雁实现该接口)
interface Flyable {
    fun fly() // 飞翔行为
}

3.2 抽象父类:动物

/**
 * 动物(抽象类,类名非斜体?若 UML 中是抽象类,需加 `abstract`)
 * - 属性:有生命(hasLife)
 * - 抽象方法:新陈代谢(依赖氧气、水)、繁殖
 */
abstract class Animal {
    var hasLife: Boolean = true // 属性:有生命
 
    // 抽象方法:新陈代谢(依赖 Oxygen 和 Water)
    abstract fun metabolism(oxygen: Oxygen, water: Water)
 
    // 抽象方法:繁殖
    abstract fun reproduce()
}

3.3 子类:鸟(继承动物,组合翅膀)

/**
 * 鸟(继承 Animal,组合 Wing)
 * - 属性:羽毛(hasFeather)、有角质喙无牙齿(hasBeakNoTeeth)
 * - 方法:下蛋(layEgg)
 */
open class Bird : Animal() {
    // 组合关系:鸟包含翅膀(生命周期与鸟绑定,构造时初始化)
    private val wing = Wing()
 
    var hasFeather: Boolean = true // 属性:羽毛
    var hasBeakNoTeeth: Boolean = true // 属性:有角质喙,没有牙齿
 
    override fun metabolism(oxygen: Oxygen, water: Water) {
        println("鸟通过翅膀辅助,进行新陈代谢,消耗氧气和水")
    }
 
    override fun reproduce() {
        layEgg() // 繁殖行为:下蛋
    }
 
    open fun layEgg() {
        println("鸟下蛋")
    }
}

3.4 具体子类:企鹅、鸭、大雁

/**
 * 企鹅(继承 Bird,关联 Climate)
 * - 关联关系:持有 Climate 引用
 */
class Penguin : Bird() {
    var climate: Climate? = null // 关联气候
 
    override fun layEgg() {
        println("企鹅在「${climate?.weather ?: "未知"}」气候下下蛋")
    }
}
 
/**
 * 鸭(继承 Bird,开放给唐老鸭继承)
 */
open class Duck : Bird() {
    override fun layEgg() {
        println("鸭下蛋")
    }
}
 
/**
 * 大雁(继承 Bird,实现 Flyable 接口)
 */
class WildGoose : Bird(), Flyable {
    override fun fly() {
        println("大雁展开翅膀,振翅高飞")
    }
 
    override fun layEgg() {
        println("大雁下蛋")
    }
}

3.5 特殊子类:唐老鸭(继承鸭)

/**
 * 唐老鸭(继承 Duck,扩展讲话能力)
 */
class DonaldDuck : Duck() {
    fun speak() {
        println("唐老鸭讲话:好啦好啦~")
    }
}

3.6 聚合类:雁群(包含大雁)

/**
 * 雁群(聚合 WildGoose,大雁可独立存在)
 */
class WildGooseFlock {
    private val geese = mutableListOf<WildGoose>() // 聚合大雁
 
    fun addGoose(goose: WildGoose) {
        geese.add(goose)
    }
 
    fun flyInVFormation() {
        println("雁群以 V 形飞行,共 ${geese.size} 只大雁参与")
    }
 
    fun flyInLineFormation() {
        println("雁群以一形飞行,共 ${geese.size} 只大雁参与")
    }
}

3.7 测试主函数

fun main() {
    // 1. 初始化依赖/关联对象
    val oxygen = Oxygen()
    val water = Water()
    val climate = Climate()
 
    // 2. 测试「鸟」的行为
    val bird = Bird()
    bird.metabolism(oxygen, water) // 新陈代谢
    bird.reproduce() // 繁殖(下蛋)
 
    // 3. 测试「企鹅」(关联气候)
    val penguin = Penguin()
    penguin.climate = climate // 关联气候
    penguin.metabolism(oxygen, water)
    penguin.reproduce()
 
    // 4. 测试「鸭」和「唐老鸭」
    val duck = Duck()
    duck.metabolism(oxygen, water)
    duck.reproduce()
 
    val donald = DonaldDuck()
    donald.metabolism(oxygen, water)
    donald.reproduce()
    donald.speak() // 唐老鸭讲话
 
    // 5. 测试「大雁」和「雁群」(聚合 + 接口实现)
    val goose1 = WildGoose()
    val goose2 = WildGoose()
    val flock = WildGooseFlock()
    flock.addGoose(goose1)
    flock.addGoose(goose2)
 
    flock.flyInVFormation() // 雁群 V 形飞行
    flock.flyInLineFormation() // 雁群一形飞行
    goose1.fly() // 大雁实现 Flyable 接口,调用 fly()
}

3.8 输出结果

在这里插入图片描述

3.9 总结 & UML关系验证

UML 关系代码体现
继承Bird : Animal、Penguin : Bird、Duck : Bird、DonaldDuck : Duck
实现WildGoose : Flyable
组合Bird 内部持有 private val wing = Wing()(生命周期绑定)
聚合WildGooseFlock 持有 MutableList(大雁可独立存在)
依赖Animal.metabolism(oxygen: Oxygen, water: Water)(参数依赖)
关联Penguin.climate: Climate?(持有 Climate 引用)