嵌入式C语言高级编程之MVC设计模式

嵌入式C语言高级编程之MVC设计模式

这是一个极简的MVC模式实现,专注于核心概念,代码简洁易懂。

1. 📁 项目结构

复制代码
minimal_mvc/
├── model.h
├── model.c
├── view.h
├── view.c
├── controller.h
├── controller.c
├── main.c
├── Makefile
└── README.md

2. 📝 代码实现

2.1 模型层 (Model) - 数据与业务逻辑

2.1.1 model.h
c 复制代码
#ifndef MODEL_H
#define MODEL_H

// 模型数据结构
typedef struct {
    int counter;      // 计数器值
    int max_value;    // 最大值
    int min_value;    // 最小值
} Model;

// 模型接口
void model_init(Model *m);
void model_increment(Model *m);
void model_decrement(Model *m);
int model_get_value(Model *m);
void model_reset(Model *m);

#endif
2.1.2 model.c
c 复制代码
#include "model.h"
#include <stdio.h>

void model_init(Model *m) {
    m->counter = 0;
    m->max_value = 10;
    m->min_value = -10;
    printf("[Model] 初始化完成,初始值: %d\n", m->counter);
}

void model_increment(Model *m) {
    if (m->counter < m->max_value) {
        m->counter++;
        printf("[Model] 值增加: %d\n", m->counter);
    } else {
        printf("[Model] 已达最大值: %d\n", m->max_value);
    }
}

void model_decrement(Model *m) {
    if (m->counter > m->min_value) {
        m->counter--;
        printf("[Model] 值减少: %d\n", m->counter);
    } else {
        printf("[Model] 已达最小值: %d\n", m->min_value);
    }
}

int model_get_value(Model *m) {
    return m->counter;
}

void model_reset(Model *m) {
    m->counter = 0;
    printf("[Model] 值已重置: %d\n", m->counter);
}

2.2 视图层 (View) - 显示界面

2.2.1 view.h
c 复制代码
#ifndef VIEW_H
#define VIEW_H

#include "model.h"

// 视图接口
void view_init(void);
void view_display(Model *m);
void view_show_message(const char *msg);

#endif
2.2.2 view.c
c 复制代码
#include "view.h"
#include <stdio.h>

void view_init(void) {
    printf("\n");
    printf("╔══════════════════════════════╗\n");
    printf("║     MVC 计数器示例          ║\n");
    printf("╚══════════════════════════════╝\n");
    printf("\n");
}

void view_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);
}

void view_show_message(const char *msg) {
    printf("\n[消息] %s\n", msg);
}

2.3 控制器层 (Controller) - 业务逻辑协调

2.3.1 controller.h
c 复制代码
#ifndef CONTROLLER_H
#define CONTROLLER_H

#include "model.h"

// 事件类型
typedef enum {
    EVENT_NONE,
    EVENT_INCREMENT,
    EVENT_DECREMENT,
    EVENT_RESET,
    EVENT_QUIT
} Event;

// 控制器接口
void controller_init(Model *m);
void controller_handle_event(Event e);
int controller_should_quit(void);
void controller_run(void);  // 主循环

#endif
2.3.2 controller.c
c 复制代码
#include "controller.h"
#include "view.h"
#include <stdio.h>
#include <stdlib.h>

static Model g_model;      // 模型实例
static int quit_flag = 0;  // 退出标志

void controller_init(Model *m) {
    if (m) {
        g_model = *m;
    } else {
        model_init(&g_model);
    }
    view_init();
    quit_flag = 0;
}

void controller_handle_event(Event e) {
    int need_refresh = 1;
    
    switch (e) {
        case EVENT_INCREMENT:
            model_increment(&g_model);
            break;
            
        case EVENT_DECREMENT:
            model_decrement(&g_model);
            break;
            
        case EVENT_RESET:
            model_reset(&g_model);
            break;
            
        case EVENT_QUIT:
            view_show_message("正在退出系统...");
            quit_flag = 1;
            need_refresh = 0;
            break;
            
        default:
            need_refresh = 0;
            break;
    }
    
    if (need_refresh) {
        view_display(&g_model);
    }
}

int controller_should_quit(void) {
    return quit_flag;
}

// 获取用户输入(阻塞式)
static Event get_user_input(void) {
    int ch = getchar();
    
    // 清除缓冲区
    while (getchar() != '\n');
    
    switch (ch) {
        case '+':
        case '=':
            return EVENT_INCREMENT;
        case '-':
            return EVENT_DECREMENT;
        case 'r':
        case 'R':
            return EVENT_RESET;
        case 'q':
        case 'Q':
            return EVENT_QUIT;
        default:
            return EVENT_NONE;
    }
}

void controller_run(void) {
    view_display(&g_model);
    
    while (!controller_should_quit()) {
        Event e = get_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
c 复制代码
#include "controller.h"

int main(void) {
    // 初始化控制器(内部会初始化模型和视图)
    controller_init(NULL);
    
    // 运行主循环
    controller_run();
    
    printf("\n程序已退出,再见!\n");
    return 0;
}

3. 🔧 Makefile

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 help

4. 🚀 编译和运行

bash 复制代码
# 编译
make

# 运行
make run

# 或者直接运行
./mvc_demo

5. 💡 运行示例

复制代码
╔══════════════════════════════╗
║     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 代码对应关系
c 复制代码
// 用户输入: '+' 键
// ↓
Event e = get_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
  • 可独立测试
  • 数据验证在这里完成

示例代码

c 复制代码
void model_increment(Model *m) {
    if (m->counter < m->max_value) {  // 业务规则
        m->counter++;                  // 数据更新
    }
}
6.3.2 View (视图层)

职责

  • 显示数据给用户
  • 接收用户输入(通常转给Controller)
  • 提供友好的用户界面

特点

  • 只读取Model,不修改
  • 可以有不同的显示方式
  • 独立于业务逻辑

示例代码

c 复制代码
void view_display(Model *m) {
    printf("当前值: %d\n", m->counter);  // 只读Model
}
6.3.3 Controller (控制器层)

职责

  • 接收用户输入
  • 调用Model更新数据
  • 触发View刷新

特点

  • 连接Model和View
  • 包含应用流程控制
  • 协调各组件交互

示例代码

c 复制代码
void controller_handle_event(Event e) {
    switch (e) {
        case EVENT_INCREMENT:
            model_increment(&g_model);  // 更新Model
            view_display(&g_model);     // 刷新View
            break;
    }
}

7. 🎯 关键设计原则

7.1 关注点分离

c 复制代码
// ❌ 错误示例:混合所有逻辑
void bad_example() {
    int counter = 0;
    char input;
    
    while(1) {
        printf("Counter: %d\n", counter);
        input = getchar();
        if (input == '+') counter++;
        if (input == '-') counter--;
        if (input == 'q') break;
    }
}

// ✅ MVC正确示例:职责分离
// Model: 存储counter
// View: 显示counter
// Controller: 处理输入并协调

7.2 单向数据流

复制代码
User Action → Controller → Model → View → User
    ↑                                      ↓
    └──────────────────────────────────────┘

7.3 依赖方向

复制代码
Controller → Model
Controller → View
Model  ↛  Controller
View   ↛  Controller

8. 🔄 扩展性示例

8.1 添加新功能 - 乘2功能

8.1.1 修改 Model
c 复制代码
// model.h 添加
void model_multiply_by_two(Model *m);

// model.c 实现
void model_multiply_by_two(Model *m) {
    int new_value = m->counter * 2;
    if (new_value <= m->max_value) {
        m->counter = new_value;
        printf("[Model] 值乘2: %d\n", m->counter);
    } else {
        printf("[Model] 乘2会超出最大值: %d\n", m->max_value);
    }
}
8.1.2 修改 Controller
c 复制代码
// controller.h 添加事件
typedef enum {
    // ... 原有事件
    EVENT_MULTIPLY_2,
} Event;

// controller.c 添加处理
case EVENT_MULTIPLY_2:
    model_multiply_by_two(&g_model);
    break;

// 修改输入处理
static Event get_user_input(void) {
    int ch = getchar();
    switch (ch) {
        // ... 原有case
        case '*':
        case 'x':
            return EVENT_MULTIPLY_2;
    }
}
8.1.3 修改 View
c 复制代码
// view.c 更新帮助信息
printf("  [+] 增加  [-] 减少  [*] 乘2  [r] 重置  [q] 退出\n");

9. 📊 性能考虑

9.1 嵌入式环境优化建议

c 复制代码
// 1. 避免不必要的刷新
static int last_value = 0;
void view_display_optimized(Model *m) {
    if (m->counter != last_value) {  // 只在值变化时刷新
        last_value = m->counter;
        printf("\r当前值: %d  ", m->counter);
        fflush(stdout);
    }
}

// 2. 使用静态缓冲区减少内存分配
static char display_buffer[64];
void view_format(Model *m) {
    snprintf(display_buffer, sizeof(display_buffer), 
             "Value: %d", m->counter);
    // 然后一次性输出
}

// 3. 事件驱动代替轮询
// 使用中断或回调,而不是不断轮询输入

10. 🧪 单元测试示例

c 复制代码
// test_model.c
#include "model.h"
#include <assert.h>

void test_model_increment() {
    Model m;
    model_init(&m);
    
    assert(model_get_value(&m) == 0);
    
    model_increment(&m);
    assert(model_get_value(&m) == 1);
    
    // 测试边界
    for(int i = 0; i < 20; i++) {
        model_increment(&m);
    }
    assert(model_get_value(&m) == 10);  // 不应超过最大值
}

int main() {
    test_model_increment();
    printf("所有测试通过!\n");
    return 0;
}

11. 📝 总结

这个最小化MVC示例展示了:

  1. 清晰的职责分离:每个组件职责明确
  2. 低耦合:组件间通过接口交互
  3. 高内聚:相关功能集中在一起
  4. 易扩展:添加新功能只需修改少量代码
  5. 可测试:Model可以独立测试

适用场景

  • 嵌入式GUI应用
  • 人机交互界面
  • 需要频繁修改界面的项目
  • 多人协作开发的项目

这个模式特别适合嵌入式系统,因为它让代码结构清晰,便于维护和测试,同时保持了代码的简洁性。

相关推荐
jolimark2 小时前
C语言存在的问题及Zig语言如何改进,差异对比全在这
c语言·内存管理·系统编程·类型系统·zig语言
青梅橘子皮2 小时前
C语言---指针的应用以及一些面试题
c语言·开发语言·算法
零号全栈寒江独钓5 小时前
基于c/c++实现linux/windows跨平台获取ntp网络时间戳
linux·c语言·c++·windows
张涛酱1074568 小时前
Subagent Orchestration 深入解析:多Agent协作的层级架构
spring·设计模式·ai编程
爱编码的小八嘎8 小时前
C语言完美演绎8-10
c语言
xxjj998a9 小时前
【Spring】Spring MVC案例
java·spring·mvc
小江的记录本11 小时前
【系统设计】《2026高频经典系统设计题》(秒杀系统、短链接系统、订单系统、支付系统、IM系统、RAG系统设计)(完整版)
java·后端·python·安全·设计模式·架构·系统架构
爱编码的小八嘎11 小时前
C语言完美演绎8-4
c语言
楼田莉子12 小时前
同步/异步日志系统:日志器管理器模块\全局接口\性能测试
linux·服务器·开发语言·c++·后端·设计模式