嵌入式C语言高级编程之MVC设计模式
嵌入式C语言高级编程之MVC设计模式这是一个极简的MVC模式实现专注于核心概念代码简洁易懂。1. 项目结构minimal_mvc/ ├── model.h ├── model.c ├── view.h ├── view.c ├── controller.h ├── controller.c ├── main.c ├── Makefile └── README.md2. 代码实现2.1 模型层 (Model) - 数据与业务逻辑2.1.1 model.h#ifndefMODEL_H#defineMODEL_H// 模型数据结构typedefstruct{intcounter;// 计数器值intmax_value;// 最大值intmin_value;// 最小值}Model;// 模型接口voidmodel_init(Model*m);voidmodel_increment(Model*m);voidmodel_decrement(Model*m);intmodel_get_value(Model*m);voidmodel_reset(Model*m);#endif2.1.2 model.c#includemodel.h#includestdio.hvoidmodel_init(Model*m){m-counter0;m-max_value10;m-min_value-10;printf([Model] 初始化完成初始值: %d\n,m-counter);}voidmodel_increment(Model*m){if(m-counterm-max_value){m-counter;printf([Model] 值增加: %d\n,m-counter);}else{printf([Model] 已达最大值: %d\n,m-max_value);}}voidmodel_decrement(Model*m){if(m-counterm-min_value){m-counter--;printf([Model] 值减少: %d\n,m-counter);}else{printf([Model] 已达最小值: %d\n,m-min_value);}}intmodel_get_value(Model*m){returnm-counter;}voidmodel_reset(Model*m){m-counter0;printf([Model] 值已重置: %d\n,m-counter);}2.2 视图层 (View) - 显示界面2.2.1 view.h#ifndefVIEW_H#defineVIEW_H#includemodel.h// 视图接口voidview_init(void);voidview_display(Model*m);voidview_show_message(constchar*msg);#endif2.2.2 view.c#includeview.h#includestdio.hvoidview_init(void){printf(\n);printf(╔══════════════════════════════╗\n);printf(║ MVC 计数器示例 ║\n);printf(╚══════════════════════════════╝\n);printf(\n);}voidview_display(Model*m){printf(\n);printf(┌──────────────────────────────┐\n);printf(│ 当前计数值: %3d │\n,m-counter);printf(│ 范围: [%d, %d] │\n,m-min_value,m-max_value);printf(└──────────────────────────────┘\n);printf(\n);printf(操作说明:\n);printf( [] 增加 [-] 减少 [r] 重置 [q] 退出\n);printf(请输入命令: );fflush(stdout);}voidview_show_message(constchar*msg){printf(\n[消息] %s\n,msg);}2.3 控制器层 (Controller) - 业务逻辑协调2.3.1 controller.h#ifndefCONTROLLER_H#defineCONTROLLER_H#includemodel.h// 事件类型typedefenum{EVENT_NONE,EVENT_INCREMENT,EVENT_DECREMENT,EVENT_RESET,EVENT_QUIT}Event;// 控制器接口voidcontroller_init(Model*m);voidcontroller_handle_event(Event e);intcontroller_should_quit(void);voidcontroller_run(void);// 主循环#endif2.3.2 controller.c#includecontroller.h#includeview.h#includestdio.h#includestdlib.hstaticModel g_model;// 模型实例staticintquit_flag0;// 退出标志voidcontroller_init(Model*m){if(m){g_model*m;}else{model_init(g_model);}view_init();quit_flag0;}voidcontroller_handle_event(Event e){intneed_refresh1;switch(e){caseEVENT_INCREMENT:model_increment(g_model);break;caseEVENT_DECREMENT:model_decrement(g_model);break;caseEVENT_RESET:model_reset(g_model);break;caseEVENT_QUIT:view_show_message(正在退出系统...);quit_flag1;need_refresh0;break;default:need_refresh0;break;}if(need_refresh){view_display(g_model);}}intcontroller_should_quit(void){returnquit_flag;}// 获取用户输入阻塞式staticEventget_user_input(void){intchgetchar();// 清除缓冲区while(getchar()!\n);switch(ch){case:case:returnEVENT_INCREMENT;case-:returnEVENT_DECREMENT;caser:caseR:returnEVENT_RESET;caseq:caseQ:returnEVENT_QUIT;default:returnEVENT_NONE;}}voidcontroller_run(void){view_display(g_model);while(!controller_should_quit()){Event eget_user_input();if(e!EVENT_NONE){controller_handle_event(e);}else{// 无效输入提示printf(\n[提示] 无效命令请使用 - r q\n);view_display(g_model);}}}2.4 主程序2.4.1 main.c#includecontroller.hintmain(void){// 初始化控制器内部会初始化模型和视图controller_init(NULL);// 运行主循环controller_run();printf(\n程序已退出再见\n);return0;}3. Makefile# 最小化MVC示例 Makefile CC gcc CFLAGS -Wall -Wextra -g TARGET mvc_demo SOURCES main.c model.c view.c controller.c OBJECTS $(SOURCES:.c.o) all: $(TARGET) $(TARGET): $(OBJECTS) $(CC) $(CFLAGS) -o $ $^ %.o: %.c $(CC) $(CFLAGS) -c $ -o $ clean: rm -f $(OBJECTS) $(TARGET) run: $(TARGET) ./$(TARGET) help: echo 可用命令: echo make - 编译程序 echo make run - 编译并运行 echo make clean - 清理编译文件 .PHONY: all clean run help4. 编译和运行# 编译make# 运行makerun# 或者直接运行./mvc_demo5. 运行示例╔══════════════════════════════╗ ║ MVC 计数器示例 ║ ╚══════════════════════════════╝ ┌──────────────────────────────┐ │ 当前计数值: 0 │ │ 范围: [-10, 10] │ └──────────────────────────────┘ 操作说明: [] 增加 [-] 减少 [r] 重置 [q] 退出 请输入命令: [Model] 值增加: 1 ┌──────────────────────────────┐ │ 当前计数值: 1 │ │ 范围: [-10, 10] │ └──────────────────────────────┘ 操作说明: [] 增加 [-] 减少 [r] 重置 [q] 退出 请输入命令: [Model] 值增加: 2 ┌──────────────────────────────┐ │ 当前计数值: 2 │ │ 范围: [-10, 10] │ └──────────────────────────────┘ 操作说明: [] 增加 [-] 减少 [r] 重置 [q] 退出 请输入命令: r [Model] 值已重置: 0 ┌──────────────────────────────┐ │ 当前计数值: 0 │ │ 范围: [-10, 10] │ └──────────────────────────────┘ 操作说明: [] 增加 [-] 减少 [r] 重置 [q] 退出 请输入命令: q [消息] 正在退出系统... 程序已退出再见6. MVC模式详解6.1 架构图┌─────────────────────────────────────────────┐ │ MAIN │ │ (程序入口和组装) │ └──────────────────┬──────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────┐ │ CONTROLLER │ │ (业务逻辑协调者) │ │ - 接收用户输入 │ │ - 调用Model更新数据 │ │ - 通知View刷新显示 │ └──────────┬──────────────────┬───────────────┘ │ │ ▼ ▼ ┌──────────────────┐ ┌──────────────────┐ │ MODEL │ │ VIEW │ │ (数据与逻辑) │ │ (界面显示) │ │ - 存储数据 │ │ - 显示数据 │ │ - 业务规则 │ │ - 用户界面 │ │ - 数据验证 │ │ - 格式化输出 │ └──────────────────┘ └──────────────────┘6.2 数据流向6.2.1 用户操作流程用户输入 → Controller接收 → Model更新 → View刷新 → 用户看到新界面6.2.2 代码对应关系// 用户输入: 键// ↓Event eget_user_input();// 返回 EVENT_INCREMENT// ↓controller_handle_event(EVENT_INCREMENT);// ↓model_increment(g_model);// Model更新数据// ↓view_display(g_model);// View刷新显示6.3 各层职责详解6.3.1 Model (模型层)职责存储应用程序数据实现业务逻辑和规则提供数据访问接口特点不依赖View和Controller可独立测试数据验证在这里完成示例代码voidmodel_increment(Model*m){if(m-counterm-max_value){// 业务规则m-counter;// 数据更新}}6.3.2 View (视图层)职责显示数据给用户接收用户输入通常转给Controller提供友好的用户界面特点只读取Model不修改可以有不同的显示方式独立于业务逻辑示例代码voidview_display(Model*m){printf(当前值: %d\n,m-counter);// 只读Model}6.3.3 Controller (控制器层)职责接收用户输入调用Model更新数据触发View刷新特点连接Model和View包含应用流程控制协调各组件交互示例代码voidcontroller_handle_event(Event e){switch(e){caseEVENT_INCREMENT:model_increment(g_model);// 更新Modelview_display(g_model);// 刷新Viewbreak;}}7. 关键设计原则7.1 关注点分离// ❌ 错误示例混合所有逻辑voidbad_example(){intcounter0;charinput;while(1){printf(Counter: %d\n,counter);inputgetchar();if(input)counter;if(input-)counter--;if(inputq)break;}}// ✅ MVC正确示例职责分离// Model: 存储counter// View: 显示counter// Controller: 处理输入并协调7.2 单向数据流User Action → Controller → Model → View → User ↑ ↓ └──────────────────────────────────────┘7.3 依赖方向Controller → Model Controller → View Model ↛ Controller View ↛ Controller8. 扩展性示例8.1 添加新功能 - 乘2功能8.1.1 修改 Model// model.h 添加voidmodel_multiply_by_two(Model*m);// model.c 实现voidmodel_multiply_by_two(Model*m){intnew_valuem-counter*2;if(new_valuem-max_value){m-counternew_value;printf([Model] 值乘2: %d\n,m-counter);}else{printf([Model] 乘2会超出最大值: %d\n,m-max_value);}}8.1.2 修改 Controller// controller.h 添加事件typedefenum{// ... 原有事件EVENT_MULTIPLY_2,}Event;// controller.c 添加处理caseEVENT_MULTIPLY_2:model_multiply_by_two(g_model);break;// 修改输入处理staticEventget_user_input(void){intchgetchar();switch(ch){// ... 原有casecase*:casex:returnEVENT_MULTIPLY_2;}}8.1.3 修改 View// view.c 更新帮助信息printf( [] 增加 [-] 减少 [*] 乘2 [r] 重置 [q] 退出\n);9. 性能考虑9.1 嵌入式环境优化建议// 1. 避免不必要的刷新staticintlast_value0;voidview_display_optimized(Model*m){if(m-counter!last_value){// 只在值变化时刷新last_valuem-counter;printf(\r当前值: %d ,m-counter);fflush(stdout);}}// 2. 使用静态缓冲区减少内存分配staticchardisplay_buffer[64];voidview_format(Model*m){snprintf(display_buffer,sizeof(display_buffer),Value: %d,m-counter);// 然后一次性输出}// 3. 事件驱动代替轮询// 使用中断或回调而不是不断轮询输入10. 单元测试示例// test_model.c#includemodel.h#includeassert.hvoidtest_model_increment(){Model m;model_init(m);assert(model_get_value(m)0);model_increment(m);assert(model_get_value(m)1);// 测试边界for(inti0;i20;i){model_increment(m);}assert(model_get_value(m)10);// 不应超过最大值}intmain(){test_model_increment();printf(所有测试通过\n);return0;}11. 总结这个最小化MVC示例展示了清晰的职责分离每个组件职责明确低耦合组件间通过接口交互高内聚相关功能集中在一起易扩展添加新功能只需修改少量代码可测试Model可以独立测试适用场景嵌入式GUI应用人机交互界面需要频繁修改界面的项目多人协作开发的项目这个模式特别适合嵌入式系统因为它让代码结构清晰便于维护和测试同时保持了代码的简洁性。