我是老溫,一名熱愛學習的嵌入式工程師,關(guān)注我,一起變得更加優(yōu)秀!
前面的一系列技術(shù)文章,我們都曾多次充分說明了,在設計業(yè)務邏輯復雜的嵌入式軟件時,最好以面向?qū)ο笞鳛榛镜脑O計思想,對各個功能模塊盡可能地做好封裝與解耦。關(guān)于嵌入式 C 語言面向?qū)ο笤O計的文章,可以點擊以下鏈接進行回顧:
基于狀態(tài)機和面向?qū)ο蟮乃枷耄O計一個通用的按鍵檢測模塊。
基于面向?qū)ο蠛秃唵喂S模式,設計一個通用的 LED 顯示模塊。
面向?qū)ο笫且环N基本的設計思想,與所采用的編程語言基本無關(guān)(如果非要杠匯編和二進制,那就是你對),一個反面的例子就是,很多人在初學C++的時候,即便C++里面集成了很多面向?qū)ο笤O計的語法糖,但初學者依然會很容易把“類”當作結(jié)構(gòu)體使用。
面向?qū)ο蟮幕境霭l(fā)點是:把“數(shù)據(jù)屬性”與“數(shù)據(jù)屬性的處理方法”都封裝在一起。而我們在使用 C 語言進行嵌入式功能模塊設計的時候,因為C語言并不具備C++語言的語法糖,所以通常都會使用結(jié)構(gòu)體的方式來模擬類的設計。
因此,我們在使用OOC(Object-Oriented C Programming with ANSI -C)技術(shù)的時候,通常都會面臨以下問題:
“嗯,我們可以使用結(jié)構(gòu)體來模擬類,把所有數(shù)據(jù)變量和函數(shù)指針都放在結(jié)構(gòu)體里面進行封裝,并且把這個結(jié)構(gòu)體放在接口頭文件里面,那么問題來了,結(jié)構(gòu)體里面的成員都是public的(語法層面可以直接使用),但某些數(shù)據(jù)變量卻是private屬性(不允許被模塊外部直接調(diào)用)”
那么,應該如何解決這個問題呢?
在我以往編寫的 C 語言面向?qū)ο笪恼吕锩妫纠a的接口頭文件,通常都伴隨著這個“直接且尖銳”的問題,而通過編碼和模塊引用規(guī)范這一系列的“君子協(xié)定”,往往只能起到“防君子而不能防小人”的作用。
直到最近,我看了傻孩子大佬(公眾號:裸機思維)的一系列文章才知道,原來C語言里面有一種技法,掩碼結(jié)構(gòu)體(Masked Structure),可以為結(jié)構(gòu)體里面的private屬性變量蓋上一層蒙版,模塊使用者即使看到了結(jié)構(gòu)體的私有數(shù)據(jù)變量,也不能對其進行外部直接訪問調(diào)用。
在通用的單片機按鍵檢測模塊這篇文章里面,對于模塊的接口頭文件 key_module.h,里面兩個最主要的結(jié)構(gòu)體,key_t和key_manager_t,其內(nèi)部大量暴露了模塊的私有參數(shù)變量,如下圖所示。
模塊使用者如果想通過某些簡單直接粗暴的方式,去修改模塊的各個屬性參數(shù),是一件輕而易舉(技術(shù)上也是合情合理)的事情,因為根據(jù)接口頭文件的最小信息公開原則,放在頭文件的信息內(nèi)容,難道不是公開且供大家放心使用的么?~
(此刻,作者我,無言以對。。。)
鑒于key_module.h接口頭文件顯現(xiàn)出來的設計不足,我們可以使用掩碼結(jié)構(gòu)體對其進行改進,例如,對于原來的key_t結(jié)構(gòu)體,我們可以把私有不公開的成員,放在struct __key里面,然后 key_t 結(jié)構(gòu)體則改進成如下圖所示的方式。
然而,這種改進并未能在真正意義上掩蓋住私有成員(struct __key的信息依然表露無遺),因此,我們參考“真刀真槍模塊化(2.5)--君子協(xié)定”這篇文章,可以通過宏定義技法,把私有成員結(jié)構(gòu)體的信息,真正隱藏起來,如下圖所示。
使用以上宏定義,那么我們可以繼續(xù)對struct __key結(jié)構(gòu)體作出進一步的改進,把結(jié)構(gòu)體的聲明和成員定義,都交給了預編譯宏進行處理,具體代碼如下圖所示。
當我們需要提取結(jié)構(gòu)體成員進行使用的時候,可以使用CLASS_INTERNAL宏,該宏展開后是一種強制類型轉(zhuǎn)換,目的是可以通過已知類型的結(jié)構(gòu)體變量,來顯式調(diào)用結(jié)構(gòu)體成員,使用方式如下圖所示。
以上,就是使用掩碼結(jié)構(gòu)體技法對單片機按鍵模塊的簡單改進(以上截圖多數(shù)為偽代碼,提供一種思路),在實際的工程項目里面,推薦直接使用PLOOC,這個開源項目已經(jīng)很完美地為C語言面向?qū)ο箝_發(fā)提供了必要的OOPC模板。
感謝閱讀。