UML 2.5核心图:类图、时序图、状态机图

好,咱们正式开始第二讲。在进入RTLA之前,我强烈建议先把UML的底子打扎实。你想想看,如果连类图和时序图都画不清楚,后面做模型转换时,很容易出现语义丢失的问题。我自己带团队时,就吃过这个亏——模型画得花里胡哨,结果代码生成出来全是bug。

UML 2.5版本其实已经非常成熟了。它不像早期版本那样臃肿,核心图就那几张。今天咱们重点讲三个:类图时序图状态机图。这三张图,基本覆盖了RTLA中80%的建模场景。

类图:系统的静态骨架

类图,说白了就是描述系统中“有什么东西”以及“它们之间什么关系”。我习惯把类图比作建筑的结构图——它不关心水管怎么走,只关心承重墙在哪。

类图的核心元素就三个:

  • 类(Class):用矩形框表示,分三格——类名、属性、操作
  • 接口(Interface):用《interface》构造型标注,或者用棒棒糖表示法
  • 关系(Relationship):关联、聚合、组合、泛化、依赖

这里有个容易踩坑的地方。我曾经在项目中看到有人把聚合和组合混用。聚合是“整体-部分”的弱关系,比如车队和车——车离开车队还能跑。组合是强关系,比如人和心脏——心脏离开人就没了。嗯,这个区别在RTLA中特别重要,因为它直接影响代码生成时的生命周期管理。

重点:类图中的多重性(Multiplicity)

1..* 表示至少一个,0..* 表示零或多个。我建议你在画类图时,必须标注多重性。不标多重性的类图,在RTLA转换时会产生歧义。

来看一个简单的代码示例,对应一个“传感器”类:

// 类图对应的Java代码
public class Sensor {
    private String sensorId;
    private double readingValue;
    
    public void collectData() {
        // 采集数据逻辑
    }
    
    public boolean validateReading() {
        return readingValue > 0;
    }
}

类图的关系用线表示。泛化是空心三角实线,实现是空心三角虚线。关联是普通实线,聚合是空心菱形,组合是实心菱形。这些符号看着多,其实用几次就记住了。

时序图:交互的时间维度

时序图,我个人的理解是——它把类图中的对象“激活”了。类图告诉你系统有哪些模块,时序图告诉你这些模块在某个场景下怎么协作。

时序图的核心元素:

  • 生命线(Lifeline):垂直虚线,代表一个对象的存在周期
  • 激活条(Execution Specification):生命线上的矩形条,表示对象正在执行操作
  • 消息(Message):水平箭头,分同步(实心箭头)、异步(开放箭头)、返回(虚线箭头)
  • 交互片段(Combined Fragment):alt(选择)、opt(可选)、loop(循环)

我记得有一次做车载系统建模,团队里有人把同步消息画成了异步。结果代码生成后,两个线程之间出现了死锁。排查了两天才发现,原来是时序图上的箭头画错了。所以,箭头类型不是装饰,是语义

我的习惯:画时序图时,先画主场景(happy path),再画异常场景。alt片段用来处理分支,opt片段用来处理可选步骤。loop片段要标注循环条件,不然别人看不懂。

时序图还有一个容易被忽略的点:时间约束。在RTLA中,我们经常需要标注“这个操作必须在10ms内完成”。UML 2.5支持在生命线上加时间标记,比如 {t=0}、{t<10ms}。这个在实时系统建模中非常实用。

状态机图:行为的状态流转

状态机图,说白了就是描述一个对象“一生中会经历哪些状态,以及怎么切换”。我特别喜欢用状态机图来建模控制逻辑,因为它比流程图更严谨。

状态机图的核心元素:

  • 状态(State):圆角矩形,分初始状态(实心圆)、普通状态、最终状态(实心圆加外圈)
  • 转移(Transition):带箭头的线,标注格式为“事件[守卫条件]/动作”
  • 伪状态(Pseudo State):选择点(菱形)、历史状态(H)、进入/退出节点

这里有个关键点:状态和动作的区别。状态是“持续存在”的,动作是“瞬间发生”的。我曾经在项目中看到有人把“发送数据”画成一个状态,这是不对的。发送数据是一个动作,应该放在转移上,或者作为状态的do活动。

避坑指南:我曾经在一个航空电子项目中,把状态机的守卫条件写成了“dataReady == true”,结果因为浮点数比较精度问题,状态永远跳不过去。后来我改用“dataReady == 1”才解决。嗯,状态机里的守卫条件,尽量用枚举或整数,别用浮点。

状态机图支持嵌套状态(Composite State)。比如一个“运行”状态,内部可以再分“初始化”、“正常”、“降级”三个子状态。这种层次结构在RTLA中特别有用,可以降低模型的复杂度。

来看一个简单的状态机示例,对应一个“门禁系统”:

// 状态机对应的伪代码
state Idle {
    on entry: 关闭门锁
    on cardSwipe: 进入验证状态
}

state Verifying {
    on entry: 读取卡片信息
    on cardValid [权限通过] -> Open
    on cardValid [权限拒绝] -> Idle
    on timeout -> Idle
}

state Open {
    on entry: 打开门锁,启动计时器
    on doorClosed -> Idle
    on timeout -> Idle
}

状态机图还有一个高级特性:协议状态机。它只关注接口的调用顺序,不关注内部实现。这个在RTLA中用于定义组件之间的交互协议,非常实用。

三张图的关系

最后,我总结一下这三张图在RTLA中的定位:

图类型 关注点 RTLA中的用途
类图 静态结构 定义组件、接口、数据流
时序图 动态交互 描述场景、验证时序约束
状态机图 行为状态 建模控制逻辑、生命周期

你想想看,一个完整的RTLA模型,通常先用类图搭骨架,再用状态机图描述核心控制逻辑,最后用时序图验证关键场景。这三张图配合使用,基本能覆盖从架构到实现的全部建模需求。

嗯,这一讲的内容就到这里。记住,UML不是画图比赛,而是沟通工具。画得再漂亮,如果语义不准确,也是白搭。下一讲,我们会深入RTLA的核心概念,到时候你会看到这些UML图是怎么“活”起来的。


公众号:蓝海资料掘金营,微信deep3321