嵌入式C語言編程:策略模式、狀態模式和狀態機的應用

概述

在沒有面向對象語法的C語言中,策略(Strategy)模式和狀態(State)模式都通過“上下文 + 接口”組合來模擬多態。
它們在代碼結構上幾乎一致,但設計意圖和應用場景卻差異很大。

本文分三部分深入剖析:

  1. 在C語言中如何模擬多態
  2. 策略模式與狀態模式的設計意圖與差異
  3. 結合EEPROM抽象層案例,展示二者協作與狀態機應用

1. C語言中的“模擬多態”

在C語言中,多態可通過結構體與函數指針模擬:上下文(Context)持有一組函數指針(vtable),動態指向具體實現。下例展示通用框架:

// 通用接口類型(vtable)
typedef struct {void (*action)(void *ctx, int evt);
} module_iface_t;// 通用上下文
typedef struct {const module_iface_t *iface;  // 指向當前策略或狀態的 vtablevoid *instance_data;          // 指向具體策略/狀態實例私有數據
} module_ctx_t;

在策略模式中,action 對應具體算法;在狀態模式中,action 則為事件處理函數。此框架既可用于策略,也可用于狀態,賦予系統極高的復用性。

2. 策略模式 vs. 狀態模式

2.1 策略模式:關注“如何做”

  • 目的:封裝一組可互換的算法,讓調用者在運行時自行選擇
  • 決策權:在外部,上下文被動執行指定策略
  • 應用場景:濾波算法切換、通信協議選擇、后端存儲后備機制等

示例類圖

sensor_t
+filter_strategy_t *strat
+process()
?interface?
filter_strategy_t
+filter(buf, len)
mean_filter
kalman_filter

2.2 狀態模式:關注“何時做、做什么”

  • 目的:將對象行為與內部狀態綁定,使行為隨狀態自動切換
  • 決策權:在內部,狀態自身決定是否及何時轉換
  • 應用場景:設備驅動、協議狀態機、時序復雜的后端服務等

示例類圖與狀態圖

usb_dev_t
+usb_state_t *state
+handle_event(evt)
?interface?
usb_state_t
+handle(dev, evt)
st_disconnected
st_connecting
st_ready
st_error
EVT_PLUG_IN
EVT_ENUM_OK
EVT_ENUM_FAIL
EVT_PLUG_OUT
EVT_PLUG_OUT
Disconnected
Connecting
Ready
Error

核心差異

  • 策略模式:算法平行,可在任意時刻替換
  • 狀態模式:狀態有序,轉換邏輯由狀態機管理

2.3 小結:模式賦能架構

在前文中,我們分別闡述了策略模式如何將“如何做”的決策權交給外部,做到算法與調用邏輯的徹底解耦;以及狀態模式如何把“何時做、做什么”的流程控制封裝到內部狀態機中,保障行為隨狀態演進而自動切換。

既然二者在實現上高度相似,我們有時候可以將二者組合起來使用。

3. 實戰:高可靠EEPROM抽象層

下面通過分層設計,展示策略模式與狀態機的協同應用。

3.1 統一層:策略模式應用

需求

為應用暴露統一接口,支持I2C EEPROM與NOR Flash兩種后端,具備自動檢測與運行時降級能力。

設計
  • 上下文unified_eeprom
  • 策略接口:包含 read_var, write_var 等函數指針
  • 具體策略I2C_EEPROM_ServiceEE_NOR_Flash_Service
  • 決策枚舉eeprom_backend_t { AUTO, I2C, FLASH, HYBRID }
// 策略接口定義
typedef struct {rt_err_t (*read_var)(int id, void *buf, size_t len);rt_err_t (*write_var)(int id, const void *buf, size_t len);
} eeprom_iface_t;// 統一上下文
typedef struct {const eeprom_iface_t *iface;eeprom_backend_t current_backend;
} unified_eeprom_t;
運行時降級
eeprom_set_backend(EEPROM_BACKEND_AUTO);rt_err_t rc = eeprom_read_var(VAR_ID_X, buf, len);
if (rc != RT_EOK) {// 失敗后自動切換策略eeprom_set_backend(EEPROM_BACKEND_FLASH);rc = eeprom_read_var(VAR_ID_X, buf, len);
}

AUTO模式下,統一層在I2C失敗時自動切換至Flash。此決策過程對上層透明,上層僅感知最終結果。

3.2 后端服務:HSM驅動的狀態模式

將I2C和Flash服務均設計為分層狀態機,封裝所有時序邏輯,實現可恢復與高可靠。

3.2.1 I2C EEPROM狀態機
  • 關鍵狀態UNINITIALIZEDIDLEREADINGWRITINGWAITING_READYVERIFYINGERROR
  • 核心邏輯:跨頁讀取、分頁寫入+輪詢就緒、可選校驗與指數退避重試
init_ok
EVT_READ_REQ
EVT_WRITE_REQ
page_done
ready
timeout
verify_ok
verify_fail
read_done
UNINITIALIZED
IDLE
READING
WRITING
WAITING_READY
VERIFYING
ERROR
3.2.2 NOR Flash狀態機
  • 關鍵狀態UNINITIALIZEDVALID_PAGEMIGRATINGFORMATTINGERROR
  • 核心邏輯:雙頁交替寫、數據遷移、啟動恢復與磨損均衡
init_ok
page_full
migration_done
write_fail
recoverable
format_done
UNINITIALIZED
VALID_PAGE
MIGRATING
ERROR
FORMATTING

4. 協同:策略 × 狀態機

在“高可靠EEPROM抽象層”案例中,我們已經看到了策略模式與狀態機的協同:

  • 策略層 (統一層):決定“走哪條路徑”,即選擇I2C后端還是Flash后端。
  • 狀態機層 (后端服務):負責“路徑內部的時序與恢復”,如I2C的寫后輪詢、Flash的頁面遷移。

兩者協同時,可在復雜約束下實現透明降級與高可用。

Appunified_eeprom(Strategy)I2C_Service(HSM)Flash_Service(HSM)eeprom_read_var(id, buf, len)策略=I2C優先ee_read_var(...)RT_ERROR觸發降級策略fee_read_var(...)RT_EOKRT_EOKAppunified_eeprom(Strategy)I2C_Service(HSM)Flash_Service(HSM)

這種“策略包含狀態機”的組合非常強大,下面我們深入探討其兩種主要協同模式。

4.1 模式一:將“完整狀態機”作為策略單元

這是EEPROM案例的核心架構模式。我們把一個“完整的狀態機(包含其所有狀態、轉移邏輯和動作)”封裝成一個策略實現。Context僅需持有指向不同狀態機策略的指針,即可在運行時“一鍵切換整體行為”。

這相當于將I2C_EEPROM_ServiceEE_NOR_Flash_Service這兩個復雜的狀態機,包裝成符合eeprom_iface_t接口的策略單元。

unified_eeprom_t
+eeprom_iface_t *iface
+set_backend()
+read_var()
?interface?
eeprom_iface_t
+read_var()
+write_var()
I2C_EEPROM_Service
-hsm_t i2c_hsm
+read_var()
+write_var()
Flash_EEPROM_Service
-hsm_t flash_hsm
+read_var()
+write_var()
后端服務 (狀態機實現)
統一層 (策略選擇)
選擇策略
降級切換
執行具體時序
執行具體時序
I2C HSM
Flash HSM
I2C Service Strategy
unified_eeprom
Flash Service Strategy

適用場景

  • 復雜流程的替換:將“I2C讀寫流程”與“Flash讀寫流程”抽象為可互換的策略。
  • 行為隔離:兩套狀態機邏輯完全獨立,互不干擾,便于獨立開發與測試。

4.2 模式二:在“狀態內部”嵌入子策略

當狀態機的主流程固定,但某個具體狀態的“動作”或“算法”需要靈活替換時,可以在該狀態內部嵌入一個“子策略”。

例如,在Flash_EEPROM_ServiceMIGRATING狀態中,數據校驗可以是一個子策略。我們可以根據系統要求,在“快速校驗(CRC8)”和“高可靠校驗(CRC32)”之間切換,而無需改變狀態機的主體結構。

// MIGRATING狀態的動作函數
void on_migrating_entry(hsm_t *sm, const hsm_event_t *event) {// ...// 根據運行時配置選擇校驗策略if (is_fast_mode()) {ctx->crc_strategy = &crc8_strategy;} else {ctx->crc_strategy = &crc32_strategy;}// ...
}// 在遷移過程中使用子策略
void perform_migration_step(context_t *ctx) {// ...uint32_t crc = ctx->crc_strategy->calculate(data, len);// ...
}

適用場景

  • 算法替換:狀態機結構穩定,但內部算法需要動態調整(如加密、壓縮、校驗算法)。
  • 功能開關:在不改變狀態流程的情況下,通過空策略(noop)實現功能的動態開啟或關閉。

4.3 C代碼示例:用策略管理多狀態機

下面的代碼演示了“模式一”的通用實現,即如何通過策略模式來管理和切換兩個完全獨立的狀態機流程。

// "狀態機策略"的統一接口
typedef struct sm_ctx_s sm_ctx_t;
typedef struct {void (*init)(sm_ctx_t*);void (*dispatch)(sm_ctx_t*, int event);
} sm_strategy_t;// 上下文,持有當前的狀態機策略
struct sm_ctx_s {const sm_strategy_t *ops;void *state_data; // 指向具體狀態機的私有數據
};// --- 狀態機A (例如: I2C EEPROM) ---
void sma_init(sm_ctx_t *c){ /* ... 狀態機A初始化 ... */ }
void sma_dispatch(sm_ctx_t *c, int e){ /* ... A的事件處理 ... */ }
const sm_strategy_t smA_strategy = { sma_init, sma_dispatch };// --- 狀態機B (例如: Flash EEPROM) ---
void smb_init(sm_ctx_t *c){ /* ... 狀態機B初始化 ... */ }
void smb_dispatch(sm_ctx_t *c, int e){ /* ... B的事件處理 ... */ }
const sm_strategy_t smB_strategy = { smb_init, smb_dispatch };// 應用層通過策略接口與狀態機交互
void application_logic(void) {sm_ctx_t fsm;// 根據條件選擇一個完整的狀態機流程作為當前策略if (use_i2c_backend()) {fsm.ops = &smA_strategy;} else {fsm.ops = &smB_strategy;}// 后續操作對具體狀態機無感知fsm.ops->init(&fsm);while (has_event()) {fsm.ops->dispatch(&fsm, next_event());}
}

實踐要點(結合統一EEPROM案例)

  • 策略層unified_eeprom負責選擇 I2C 或 Flash 后端(含 AUTO/降級),對上層透明。
  • 狀態機層i2c_eeprom_serviceee_nor_flash_service各自封裝頁寫、遷移、恢復等復雜時序與錯誤處理。
  • 并發與鎖序:策略層持短鎖做選擇,狀態機在各自內部串行化操作,避免跨層嵌套鎖。
  • 失敗處理:策略層負責統計健康度并決策是否切換;狀態機負責局部的、可恢復的重試。

5. 小結

  • 在嵌入式C中,可通過“上下文 + 接口”框架復用策略與狀態模式實現結構。
  • 策略模式關注“如何做”,決策權在外部;狀態模式關注“何時做、做什么”,決策權在內部。
  • 將策略與狀態機結合,可構建透明、可降級、高可用的分層架構。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/95105.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/95105.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/95105.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

人工智能、機器學習、深度學習:2025技術革命的深度解析

目錄 人工智能、機器學習、深度學習:技術革命的深度解析 引言 第一部分:人工智能的起源與演進 1.1 人工智能的定義 1.2 人工智能的歷史 1.3 人工智能的關鍵概念 a.知識表示(Knowledge Representation) b.搜索算法&#xf…

【Python】常用內置模塊

1.os 文件目錄 import os# 創建文件夾 os.mkdir(dir) # 判斷文件是否存在 os.path.exists(path) # 列出文件夾下文件列表 os.listdir(dir)""" 常用 """ # 當前文件相對路徑 os.getcwd()# 當前文件絕對路徑 os.path.abspath(__file__)# 當前文…

(Python)爬蟲進階(Python爬蟲教程)(CSS選擇器)

源代碼:#導入庫 import requests from bs4 import BeautifulSoup import pandas as pd#爬蟲函數 def scrape_books():#1.基本網址連接base_url "http://books.toscrape.com"#2.獲取基本網址responserequests.get(base_url)#3.檢查是否正常訪問if respons…

第七節 自然語言處理與Bert

自然語言處理與BERT模型:從基礎到實踐入門 自然語言處理(NLP)的核心目標之一是讓計算機理解人類語言的語義和上下文。本文將從基礎的字詞表示出發,逐步解析傳統模型的局限性、Self-attention的突破性思想,以及BERT如何…

攻擊者瞄準加密技術的基礎:智能合約

雖然利用許多智能合約中的安全漏洞已經成為網絡攻擊者的長期目標,但越來越多的安全公司開始關注使用欺詐性或混淆的智能合約從加密貨幣賬戶中竊取資金的騙局。 根據網絡安全公司 SentinelOne 本周發布的分析報告,在最近一次引人注目的攻擊中&#xff0c…

基于開源AI大模型、AI智能名片與S2B2C商城小程序的零售智能化升級路徑研究

摘要:在零售業數字化轉型浪潮中,人工智能技術正從“輔助工具”向“核心生產力”演進。本文聚焦開源AI大模型、AI智能名片與S2B2C商城小程序的協同應用,提出“數據感知-關系重構-生態協同”的三維創新框架。通過分析智能傳感、動態畫像與供應鏈…

機器學習 樸素貝葉斯

目錄 一.什么是樸素貝葉斯 1.1 從 “概率” 到 “分類” 二.樸素貝葉斯的數學基礎:貝葉斯定理 2.1 貝葉斯定理公式 2.2 從貝葉斯定理到樸素貝葉斯分類 2.3 “樸素” 的關鍵:特征獨立性假設 三、樸素貝葉斯的三種常見類型 3.1 高斯樸素貝葉斯&…

A Logical Calculus of the Ideas Immanent in Nervous Activity(神經網絡早期的M-P模型)

哈嘍,各位朋友大家上午好!今天我們要一起啃下這篇神經科學與邏輯學交叉領域的奠基之作——McCulloch和Pitts的《A Logical Calculus of the Ideas Immanent in Nervous Activity》。這篇論文篇幅不長,但每一個定理、每一個假設都像精密齒輪&a…

大語言模型提示工程與應用:提示工程-提升模型準確性與減少偏見的方法

語言模型可靠性優化 學習目標 在本課程中,我們將學習通過提示工程提升模型事實準確性、減少偏見的有效方法。 相關知識點 語言模型可靠性優化 學習內容 1 語言模型可靠性優化 1.1 事實準確性增強 LLM可能生成看似合理但實際虛構的內容。優化策略包括&#x…

遇到前端導出 Excel 文件出現亂碼或文件損壞的問題

1. 檢查后端返回的數據格式確認接口響應:確保后端返回的是二進制流(如 ArrayBuffer)或 Base64 編碼的 Excel 文件,而非 JSON 字符串。用瀏覽器開發者工具(Network 標簽)檢查接口響應類型:正確的…

2025年Cloudflare WAF防護機制深度剖析:5秒盾繞過完全指南

2025年Cloudflare WAF防護機制深度剖析:5秒盾繞過完全指南 技術概述 Cloudflare作為全球領先的CDN和網絡安全服務提供商,其WAF(Web Application Firewall)防護系統已經成為現代Web安全的標桿。特別是其標志性的"5秒盾"…

【Android調用相冊、拍照、錄像】等功能的封裝

關于調用Android項目 關于Android中調用相機拍照、錄像,調用相冊選圖等是比較繁瑣的,為了減少代碼冗余,肯定需要封裝成工具類,最終使用大概如下,大部分代碼使用Java編寫,因為需要照顧到不適用kotlin的伸手…

Git 分支管理:從新開發分支遷移為主分支的完整指南

問題背景 我在使用 Git 進行開發時,由于原有的主分支遭到了污染,不得已在多方嘗試之后,決定替換原有的主分支。創建一個新分支并完成了重要修改: 基于提交 0fcb6df0f5e8caa3d853bb1f43f23cfe6d269b18 創建了 new-development 分支…

nginx常見問題(四):端口無權限

當 Nginx 日志報錯 bind() to 80 failed (13: Permission denied) 時,這通常是由于權限不足導致 Nginx 無法綁定到 80 端口(該端口為系統特權端口)。以下是詳細的問題分析與解決方案:一、問題原因分析80 端口屬于 系統特權端口&am…

【線性代數】線性方程組與矩陣——(3)線性方程組解的結構

上一節:【線性代數】線性方程組與矩陣——(2)矩陣與線性方程組的解 總目錄:【線性代數】目錄 文章目錄9. 向量組的線性相關性與線性方程組解的結構9.1. 向量組及其線性組合9.2. 向量組的線性相關性9.3. 向量組的秩9.4. 線性方程組…

機器學習-----K-means算法介紹

一、為什么需要 K-Means?在監督學習中,我們總把數據寫成 (x, y),讓模型學習 x → y 的映射。 但現實中很多數據根本沒有標簽 y,例如:啤酒:熱量、鈉含量、酒精度、價格用戶:訪問時長、點擊次數、…

Spring Security自動處理/login請求,后端控制層沒有 @PostMapping(“/login“) 這樣的 Controller 方法

一:前言 (1)Spring Security概念: Spring Security 是屬于 Spring 生態下一個功能強大且高度可定制的認證和授權框架,它不僅限于 Web 應用程序的安全性,也可以用于保護任何類型的應用程序。 &#xff08…

idea開發工具中git如何忽略編譯文件build、gradle的文件?

idea開發工具中: git顯示下面這個文件有變更: ~/Documents/wwwroot-dev/wlxl-backend/java/hyh-apis/hyh-apis-springboot/build/resources/main/mapping/AccountRealnameMapper.xml 我git的根路徑是: ~/Documents/wwwroot-dev/wlxl-backend/…

狀態機淺析

狀態機是處理狀態依賴型行為的高效工具,通過結構化建模狀態轉換,解決了傳統條件判斷的冗余和混亂問題。它在設備控制、流程管理、協議解析等場景中表現優異,核心優勢在于邏輯清晰、可擴展性強和易于調試。 一、介紹 1. 概念 狀態機&#x…

Windows 手動病毒排查指南:不依賴殺毒軟件的系統安全防護

Windows 手動病毒排查指南:不依賴殺毒軟件的系統安全防護 在數字時代,電腦病毒就像潛伏的"網絡幽靈",從竊取隱私的木馬到消耗資源的蠕蟲,時刻威脅著系統安全。當殺毒軟件失效或遭遇新型威脅時,手動排查病毒便…