每天學一個,設計模式概要
題外話
回想起大學學過一門課程《設計模式》,裏面描述了很多包含前人思想精華的設計模式,用於構建代碼。工作久了,雖然在寫嵌入式C,但也經常能發現設計模式的精妙之處。而這些構建軟件的思想,正是做底層軟件的人經常所缺乏的思想,或者說,很少有軟件復用、設計復用的意識。
比如,現在要設計一個定時器,用於計時,當計時時間滿足條件時執行某項任務。假設硬件定時器中斷1ms,任務定時1s,可以用uint32_t taskTimer(初值0) 來作軟件定時器。
常規做法:
定義硬件定時器中斷處理
// eTimer.c extern uint32_t taskTimer; // 硬件定時中斷處理函數,1ms一次interrupt void eTimerInterrupt() { taskTimer ++; // 清除中斷... }
查詢軟件定時器並執行任務
// main.c或者任務模塊 #define TASK_TIME 1000 uint32_t taskTimer = 0; //任務定時器 int main() { while(1){ if(taskTimer > TASK_TIME ) { taskTimer = 0; // 清楚定時器 //執行任務... } }return 0; }
功能上,這個做法沒有問題。但是,一旦項目的復雜度高,或者參與的人數多了,會讓別人、甚至自己都看不懂,造成很大的困擾;如果應用層代碼一旦有修改,比如換個變量名字,添加一個定時器,都需要修改底層。顯然,這是不合理的。比較合理的做法是,底層應該是調用硬件驅動,給上層以接口形式提供服務,而不是強迫底層必須了解上層的細節,修改上層時也必須修改底層。
試想,如果操作系統不得不關註應用程序具體是如何使用某項功能的,而且應用的功能修改,操作系統底層必須修改,這種設計後果有多可怕。而處理這個問題有一個很不錯的思路:就是將應用的定時器功能委托給底層硬件定時器模塊,而底層只提供接口,不關註應用的使用細節。
沒有經過封裝的用法:
經過委托模式思想封裝的用法:
參考
1. 設計模式六大原則 | 博客園
2. 設計模式六大原則
3. 設計模式六大原則 | CSDN
設計模式
總原則: 低耦合,高內聚;對擴展開放,對修改關閉。
六大原則
1. 單一職責原則
應該有且只有一個原因引起類的變化。簡單來說,一個類負責一項職責,功能要單一。
場景:比如一個程序員既要負責編碼,又要負責測試,那麽編碼出現問題的時候,就容易影響到測試;測試出問題的時候,就容易影響到編碼。如果程序員只負責一項工作,那麽就會,
優點:提高可擴展性、可維護性,降低類的復雜度。
2. 裏氏替換原則
只要父類出現的地方,一定可以使用子類,而且不會出現任何異常。這樣,使用者不需要知道是父類還是子類。
場景:商場為客戶(人)提高購物場所,任何人都可以進去購物。如果非要針對每個人的職業、年齡、身體狀況進行區分判斷、對待,那麽這個工作量無疑是巨大的。而且是不安全的,因為商場不得不針對實際情況修改驗證規則。
優點:程序健壯性增強;即使增加不同子類,不影響原類運行。
3. 依賴倒置原則
高層模塊不依賴底層模塊,二者都應該依賴抽象;抽象不應該依賴細節,細節應該依賴抽象。
簡而言之,對接口編程,而不是對實現編程。
場景:筆記本電腦雖然每個型號的充電插座端接口不一定相同,有TYPE-C的,有圓孔的,還有方口的,不過都會根據220V電源提供充電插頭(接口),否則設計比較的時候按每個筆記本不同細節來決定電源電壓,就會導致筆記本不通用,甚至難以充電。
優點:實現模塊松耦合;在大的項目效果明顯,並行開發更友好。
4.接口隔離原則
類之間的依賴關系應建立在最小的接口上。
簡而言之,接口方法盡量少而小,只留需要的,去掉不需要的。只暴露給調用他的類所需要的方法。
場景:客戶購車,只關心價格、外觀、駕駛性能等特性,如果非讓客戶去了解每個零部件供應商、工作原理,甚至制造過程,勢必會導致客戶所識車臃腫、復雜,而且對客戶的要求也更高。
優點:提高內聚,減少對外交互;
5. 迪米特法則
類間解耦。
通俗地,一個類對自己依賴的類知道的越少越好。
場景:公立銀行或醫院就是一個很好的設計,不用關心銀行/醫院的架構是怎樣的,財務報表是怎麽表現的,有國家信用支撐,會相信存在銀行的錢不會輕易坑自己,去醫院看病不會坑害自己。
優點:低耦合,易擴展。
6. 開放封閉原則
通過擴展來解決需求變化,而不是通過修改已有代碼。
場景:比如設計了一本書,已經通過出版社出版,後來發現書中內容有誤,如果要修改原書內容,就要召回所有書籍,重新印刷,但是這個工作量是巨大的,而且成本非常高。一種更好的方式是,在官網再擴展出一本勘誤,這樣有疑問的人可以去官網查詢。
優點:降低修改成本;
每天學一個,設計模式概要