• 正文
    • 1、MVC編程模型簡介
  • 相關推薦
申請入駐 產業(yè)圖譜

嵌入式編程模型 | MVC模型

03/10 16:58
1085
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

大家好,我是雜燴君。

嵌入式 / 單片機項目開發(fā)中,我們常常會根據實際情況選擇大方向的軟件框架:裸機系統(tǒng)、前后臺系統(tǒng)、RTOS、Linux等。實際開發(fā)中,選擇什么樣的軟件架構,只是第一步。

系統(tǒng)里面的各個模塊怎么協(xié)同工作,業(yè)務邏輯怎么設計?也得在項目前期好好考慮,如果是想到哪寫到哪,可能后面會受很多苦。

我們有必要學習各種編程模型了(設計模式)。如事件驅動編程模型、消息驅動編程模型、狀態(tài)機模型等。

編程模型與大方向的軟件框架并不沖突,如我們常用的狀態(tài)機模型,裸機系統(tǒng)也能跑、RTOS也能跑、Linux也能跑。

本篇筆記我們一起來學習MVC編程模型。

1、MVC編程模型簡介

MVC(Model-View-Controller)是一種軟件設計模式,它將應用程序分為三個主要部分:模型(Model)、視圖(View)和控制器(Controller),這種分離有助于提高代碼的可維護性、可擴展性和可測試性。

模型(Model):專注于數據管理和業(yè)務邏輯。

視圖(View):負責呈現數據給用戶,它是用戶界面的部分。

控制器(Controller):作為模型和視圖之間的橋梁,接收用戶的輸入請求,根據請求調用相應的模型方法進行數據處理,然后選擇合適的視圖將處理結果展示給用戶。

MVC 最初是為 Web 應用程序設計的,但它的思想同樣適用于嵌入式系統(tǒng)開發(fā)。

如嵌入式場景中:

模型(Model):處理傳感器數據采集與處理。

視圖(View):處理數據顯示(如LCD屏渲染、LED狀態(tài)指示)。

控制器(Controller):協(xié)調輸入(如GUI輸入、中斷處理、用戶按鍵響應)。

MVC的核心優(yōu)勢:

1、結構圖

單向依賴:Controller操作Model,View監(jiān)聽Model。

解耦設計:View和Controller不直接交互,Model獨立于界面邏輯。

2、時序圖

    • 用戶輸入(如按鍵)傳遞到Controller。Controller修改Model數據(如更新傳感器狀態(tài))。Model通過

notifyObservers()

    回調觸發(fā)View更新(非Controller直接調用View)。View從Model拉取最新數據并渲染(如刷新LCD顯示)。

MVC思想應用實例

下面列舉4個例子,一個是我們自己編寫的最小MVC demo,另外3個是Github上嵌入式相關的使用到MVC思想的項目。

1、MVC demo

嵌入式中,帶GUI界面的產品,會有這種場景:切換GUI界面,業(yè)務邏輯讀取本地配置數據,刷新到對應GUI界面上。

下面我們針對這種簡單的場景編寫基于一個簡單的基于pthread庫的Linux多線程應用實例:

控制器模塊:接收用戶輸入的頁面索引,根據映射關系獲取對應的配置索引,并向模型模塊發(fā)送讀取配置的請求。

模型模塊:根據接收到的配置索引,從配置數組中獲取相應的配置信息,并發(fā)送給視圖模塊。

視圖模塊:接收到配置信息后,將其顯示在對應的頁面上。

(1)類圖

類定義

Model?類負責處理配置數據的讀取和存儲,包含配置數組、線程和消息隊列等成員。

View?類負責顯示頁面和配置信息,包含當前頁面索引、線程和消息隊列等成員。

Controller?類作為協(xié)調者,持有?Model?和?View?的指針,負責接收用戶輸入并控制它們的交互。

Config?類表示配置信息,包含配置名稱和值。

關聯(lián)關系

Controller 與 Model、View 之間是聚合關系(has),即 Controller 持有 Model 和 View 的實例。Model 與 Config 之間是組合關系(contains),Model 包含多個 Config 實例。

(2)時序圖

(3)運行

    終端輸入頁面索引。Controller 接收到輸入后,根據頁面 - 配置映射關系獲取對應的配置索引。Controller 向 Model 發(fā)送 READ_CONFIG 消息,請求讀取指定配置。Model 從 Config 中獲取相應的配置信息。Model 向 View 發(fā)送 CONFIG 消息,傳遞配置信息。View 接收到消息后,顯示當前頁面和配置信息。
(4)代碼

#include<stdio.h>
#include"model.h"
#include"view.h"
#include"controller.h"

intmain(void)
{
? ? Model *model = create_model();
? ? View *view = create_view();
? ? Controller *controller = create_controller(model, view);

? ? start_model(model);
? ? start_view(view);
? ? start_controller(controller);

? ? pthread_exit(NULL);

? ??return0;
}

model.c:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include"model.h"

staticvoidupdate_config(Model *model,?int?config_index)
{
? ??const?Config *config = get_config(model->configs, config_index, model->num_configs);

? ??if?(config)?
? ? {
? ? ? ??char?msg[MAX_MSG_SIZE] = {0};

? ? ? ??snprintf(msg,?sizeof(msg),?"CONFIG:%s:%d", config->name, config->value);
? ? ? ? send_message(model->mq, msg);
? ? }
}

void*?model_thread_func(void?*arg)
{
? ? Model *model = (Model *)arg;
? ??char?buf[MAX_MSG_SIZE] = {0};

? ??while?(1)?
? ? {
? ? ? ??ssize_t?bytes_received = receive_message(model->mq, buf);
? ? ? ? buf[bytes_received] =?'';
? ? ? ??
? ? ? ??if?(strncmp(buf,?"READ_CONFIG:",?12) ==?0)?
? ? ? ? {
? ? ? ? ? ??int?config_index = atoi(buf +?12);
? ? ? ? ? ? update_config(model, config_index);
? ? ? ? }
? ? }

? ??returnNULL;
}

Model*?create_model(void)
{
? ? Model *model = (Model *)malloc(sizeof(Model));

? ??if?(model ==?NULL)?
? ? {
? ? ? ? perror("malloc");
? ? ? ??exit(EXIT_FAILURE);
? ? }

? ? model->num_configs = MAX_CONFIGS;
? ? init_configs(model->configs, model->num_configs);
? ? model->mq = init_message_queue();

? ??return?model;
}

voidstart_model(Model *model)
{
? ??if?(pthread_create(&model->thread,?NULL, model_thread_func, model) !=?0)?
? ? {
? ? ? ? perror("pthread_create");
? ? ? ??exit(EXIT_FAILURE);
? ? }
}

voidstop_model(Model *model)
{
? ? pthread_join(model->thread,?NULL);
? ? close_message_queue(model->mq);
? ??free(model);
}

view.c:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include"view.h"

staticvoiddisplay_page(View *view,?constchar?*config_name,?int?config_value)
{
? ??printf("當前頁面: %dn", view->current_page);
? ??printf("業(yè)務配置: %s, 值: %dn", config_name, config_value);
}

void*?view_thread_func(void?*arg)
{
? ? View *view = (View *)arg;
? ??char?buf[MAX_MSG_SIZE] = {0};

? ??while?(1)?
? ? {
? ? ? ??ssize_t?bytes_received = receive_message(view->mq, buf);
? ? ? ? buf[bytes_received] =?'';

? ? ? ??if?(strncmp(buf,?"CONFIG:",?7) ==?0)?
? ? ? ? {
? ? ? ? ? ??char?config_name[32] = {0};
? ? ? ? ? ??int?config_value =?0;
? ? ? ? ? ??sscanf(buf +?7,?"%[^:]:%d", config_name, &config_value);
? ? ? ? ? ? display_page(view, config_name, config_value);
? ? ? ? }
? ? }

? ??returnNULL;
}

View*?create_view(void)
{
? ? View *view = (View *)malloc(sizeof(View));

? ??if?(view ==?NULL)?
? ? {
? ? ? ? perror("malloc");
? ? ? ??exit(EXIT_FAILURE);
? ? }

? ? view->current_page =?1;
? ? view->mq = init_message_queue();

? ??return?view;
}

voidstart_view(View *view)
{
? ??if?(pthread_create(&view->thread,?NULL, view_thread_func, view) !=?0)?
? ? {
? ? ? ? perror("pthread_create");
? ? ? ??exit(EXIT_FAILURE);
? ? }
}

voidstop_view(View *view)
{
? ? pthread_join(view->thread,?NULL);
? ? close_message_queue(view->mq);

? ??free(view);
}

controller.c:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
#include"controller.h"
#include"message_queue.h"

#define?PAGE_CONFIG_MAP_SIZE 10
staticint?page_config_map[PAGE_CONFIG_MAP_SIZE] = {0};

staticvoidinit_page_config_map(void)
{
? ??for?(int?i =?0; i < PAGE_CONFIG_MAP_SIZE; i++)?
? ? {
? ? ? ? page_config_map[i] = i;
? ? }
}

void*?controller_thread_func(void?*arg)
{
? ? Controller *controller = (Controller *)arg;
? ??char?input[10] = {0};

? ? init_page_config_map();

? ??while?(1)?
? ? {
? ? ? ??printf("輸入頁面索引 (0 - %d) 顯示對應頁面配置,輸入 'q' 退出程序: ", PAGE_CONFIG_MAP_SIZE -?1);
? ? ? ? fgets(input,?sizeof(input),?stdin);
? ? ? ??if?(input[0] >=?'0'?&& input[0] <=?'9')?
? ? ? ? {
? ? ? ? ? ??int?page_index = atoi(input);

? ? ? ? ? ??if?(page_index >=?0?&& page_index < PAGE_CONFIG_MAP_SIZE)?
? ? ? ? ? ? {
? ? ? ? ? ? ? ? controller->view->current_page = page_index;
? ? ? ? ? ? ? ??int?config_index = page_config_map[page_index];

? ? ? ? ? ? ? ??char?msg[MAX_MSG_SIZE] = {0};

? ? ? ? ? ? ? ??snprintf(msg,?sizeof(msg),?"READ_CONFIG:%d", config_index);
? ? ? ? ? ? ? ? send_message(controller->model->mq, msg);
? ? ? ? ? ? }?
? ? ? ? ? ??else
? ? ? ? ? ? {
? ? ? ? ? ? ? ??printf("無效的頁面索引,請重新輸入。n");
? ? ? ? ? ? }
? ? ? ? }?
? ? ? ??elseif?(input[0] ==?'q')?
? ? ? ? {
? ? ? ? ? ? stop_model(controller->model);
? ? ? ? ? ? stop_view(controller->view);
? ? ? ? ? ? close_message_queue(controller->mq);
? ? ? ? ? ??exit(0);
? ? ? ? }
? ? }

? ??returnNULL;
}

Controller*?create_controller(Model *model, View *view)
{
? ? Controller *controller = (Controller *)malloc(sizeof(Controller));

? ??if?(controller ==?NULL)?
? ? {
? ? ? ? perror("malloc");
? ? ? ??exit(EXIT_FAILURE);
? ? }

? ? controller->model = model;
? ? controller->view = view;
? ? controller->mq = init_message_queue();

? ??return?controller;
}

voidstart_controller(Controller *controller)
{
? ??if?(pthread_create(&controller->thread,?NULL, controller_thread_func, controller) !=?0)?
? ? {
? ? ? ? perror("pthread_create");
? ? ? ??exit(EXIT_FAILURE);
? ? }
}

voidstop_controller(Controller *controller)
{
? ? pthread_join(controller->thread,?NULL);
? ? close_message_queue(controller->mq);
? ??free(controller);
}

篇幅有限,為了不影響閱讀體驗。其它代碼不再貼出。有興趣的朋友可在本公眾號聊天界面輸入關鍵詞:MVC例子,即可獲取下載鏈接。

2、Q-Controllers

Q-Controllers是一個事件驅動的應用代碼框架,適用于低端單片機無法跑操作系統(tǒng),但又要處理越來越復雜的代碼構架的情況。

https://github.com/q-iot/q-controllers

其借鑒MVC模式,提出D-IO-C分層架構(Data-IO-Controller),將代碼分為:

Data數據存儲與管理(類似Model層)

IO:輸入輸出硬件操作(如傳感器、顯示屏驅動)

Controller:事件驅動的業(yè)務邏輯處理

3、IotSensorDetect

一個基于MVC模式 + 狀態(tài)設計模式的物聯(lián)網氣體檢測開源項目。

https://github.com/Yangyuanxin/IotSensorDetect

MVC模式:

Model:傳感器數據采集與狀態(tài)管理(如ModelSensorHandlerTask)

View:數據展示(通過云平臺或LCD屏)

Controller:指令處理與狀態(tài)協(xié)調(如ControllerTask)

4、awtk的計算器例子

基于MVC開發(fā)的計算器:https://github.com/zlgopen/awtk-patterns/tree/master/src/calculator-mvc

Q & A

Q1: 業(yè)務邏輯應該寫在Controller還是Model?

A:應該寫在Model。

已上就是本次關于MVC模型的分享,如果覺得文章有幫助,麻煩幫忙轉發(fā),謝謝!

相關推薦

登錄即可解鎖
  • 海量技術文章
  • 設計資源下載
  • 產業(yè)鏈客戶資源
  • 寫文章/發(fā)需求
立即登錄

本公眾號專注于嵌入式技術,包括但不限于C/C++、嵌入式、物聯(lián)網、Linux等編程學習筆記,同時,公眾號內包含大量的學習資源。歡迎關注,一同交流學習,共同進步!