UML 类图在 C/C++ 中的工程化理解
一、设计约束(核心认知)
UML 类图不是"语法图",而是"设计意图图"
UML 中的可见性符号表达的是设计约束,而不是语言语法本身:
+:我允许你用(对外接口)-:你不该碰(内部实现细节)#:给子模块/派生模块留的
C/C++ 只是实现方式,不是 UML 的限制
二、C++ 视角下的 UML 类图
1. UML 与 C++ 的直接映射关系
| UML 概念 | C++ 对应 |
|---|---|
| 类(Class) | class |
| 属性(Attribute) | 成员变量 |
| 方法(Operation) | 成员函数 |
| 继承 | : public Base |
+ / - / # |
public / private / protected |
2. C++ 示例代码
cpp
class SerialPort {
public:
void init();
void send(uint8_t* buf, int len);
private:
int fd;
};
3. 对应的 UML 类图表示
┌────────────────────────────┐
│ SerialPort │
├────────────────────────────┤
│ - fd : int │
├────────────────────────────┤
│ + init() : void │
│ + send(buf : uint8_t*, │
│ len : int) : void │
└────────────────────────────┘
4. 设计含义说明
-
fd被标记为-:外部模块不应该直接操作底层资源 -
init()、send()被标记为+:对外提供的稳定 API
三、C 语言视角下的 UML 类图
C 语言没有 class,但 UML 允许你"逻辑上当成 class"
在 C 项目中,UML 类图表达的是模块设计意图。
1. C 语言中的工程约定
static函数/变量 → UML 中的-(private).h中暴露的 API → UML 中的+(public)struct+ 函数 → 逻辑上的一个"类"
2. C 语言示例代码
c
typedef struct {
UART_HandleTypeDef *huart;
uint8_t rxBuf[128];
uint16_t rxLen;
UartState state;
} UartDriver_t;
void Uart_Init(UartDriver_t *drv, UART_HandleTypeDef *huart);
void Uart_Send(UartDriver_t *drv, uint8_t *buf, uint16_t len);
void Uart_StartReceive(UartDriver_t *drv);
UartState Uart_GetState(UartDriver_t *drv);
3. 对应的 UML 类图表示
注意 :
UartDriver_t *drv在 UML 中是隐含的this,不需要画出来
┌────────────────────────────────────┐
│ UartDriver │
├────────────────────────────────────┤
│ - huart : UART_HandleTypeDef* │
│ - rxBuf : uint8_t[128] │
│ - rxLen : uint16_t │
│ - state : UartState │
├────────────────────────────────────┤
│ + init(huart : UART_HandleTypeDef*)│
│ + send(buf : uint8_t*, len : u16) │
│ + startReceive() : void │
│ + getState() : UartState │
└────────────────────────────────────┘
4. 设计含义说明
-
struct成员在设计上属于 private 属性- → 即使语法上可访问,也不代表设计上允许访问
-
对外函数即 UML 中的 public 方法
-
UartDriver_t *drv是 C 语言模拟的this指针
四、关键理解总结(非常重要)
-
UML 类图表达的是"边界",不是语法
-
+/-/#表达的是"谁能用",不是"怎么写" -
C 语言完全可以按照 UML 的类图思想来设计模块
-
一个设计良好的 C 模块,本质上就是一个 UML 类
补充说明
为什么这种理解很重要?
在嵌入式开发和系统编程中,即使使用 C 语言,我们仍然需要:
- 模块化设计:清晰的接口边界
- 封装性:隐藏实现细节
- 可维护性:通过设计约束减少耦合
UML 类图为我们提供了一种语言无关的设计思维方式,帮助我们在 C 语言中也能实现良好的面向对象设计原则。
实践建议
- 设计先行:先画 UML 类图,再写代码
- 注释规范:在头文件中标注哪些是公开 API,哪些是内部使用
- 命名约定 :使用命名规范来体现模块边界(如
模块名_函数名) - 代码审查:检查是否有违反设计意图的访问行为