點擊上方藍色字體了解更多的嵌入式編程實用技能。
如果你覺得該文章對你有幫助,歡迎點贊+關注
軟件開發設計中最大的難題就是應對需求的變化,而各種各樣的需求變化又是不可預料的,我們要為這種不可預料的變化做好準備,這本身是一件十分痛苦的事情,通常涉及到功能的變更、擴展和刪除等,所幸前輩們已經給我們提出了經典的六大設計原則和23種設計模式來“封裝”未來的變化。
在程序設計領域, SOLID(單一功能、開閉原則、里氏替換、接口隔離以及依賴反轉)是由羅伯特·C·馬丁在21世紀早期引入的記憶術首字母縮略字,指代了面向對象編程和面向對象設計的五個基本原則。六大設計原則中多了一個“迪米特法則”。
本文只針對六大設計原則的單一職責原則進行介紹。
六大設計原則和23種設計模式主要適用于面向對象的編程語言,而非面向過程語言,即C語言。
熟練理解6大設計原則后,在面向過程語言中也能有一定的借鑒。
在面向對象編程領域中,單一功能原則(Single responsibility principle)規定每個類都應該有一個單一的功能,并且該功能應該由這個類完全封裝起來。所有它的(這個類的)服務都應該嚴密的和該功能平行(功能平行,意味著沒有依賴)。
單一職責原則又稱單一功能原則,不要讓一個類承擔過多的職責。避免職責耦合在一起,避免一個職責的變化影響到其他職責。
所謂職責是指類變化的原因。如果一個類有多于一個的動機被改變,那么這個類就具有多于一個的職責。而單一職責原則就是指一個類或者模塊應該有且只有一個改變的原因。
一個具體的例子就是,想象有一個用于編輯和打印報表的模塊。這樣的一個模塊存在兩個改變的原因。第一,報表的內容可以改變(編輯)。第二,報表的格式可以改變(打印)。這兩方面的改變會因為完全不同的起因而發生:一個是本質的修改,一個是表面的修改。單一功能原則認為這兩方面的問題事實上是兩個分離的功能,因此他們應該分離在不同的類或者模塊里。
如果一個類或者模塊承擔的職責過多,就等于把這些職責耦合在一起了。一個職責的變化可能會削弱或者抑制這個類完成其他職責的能力。這種耦合會導致脆弱的設計,當發生變化時,設計會遭受到意想不到的破壞。而如果想要避免這種現象的發生,就要盡可能的遵守單一職責原則。
比如功能1和功能2,功能1由于需求變更導致功能1模塊需要調整,而調整后可能導致功能2原本正常的功能出現異常,這就表明功能1和功能2存在耦合
因此此原則的核心就是解耦和增強內聚性,控制類的粒度大小(模塊或函數功能的粒度大小)
C語言中可以理解為功能模塊化、單一功能文件、單一功能函數等,如實現oled功能模塊、由具體功能實現接口函數文件、硬件接口功能文件、配置文件和字體數據文件組成;同時每個文件中的每個函數只具備一個功能,如畫點、畫線、畫圓,甚至細分到光標定位函數。
其實熟練的程序設計人員都清楚應該寫出高內聚低耦合的程序,但是實際開發過程中很多耦合常常發生在不經意之間。
職責擴散:因為某種原因,某一職責被分化為顆粒度更細的多個職責了;比如光標定位函數過度地細分了多個函數進行實現
因此在實現中,也需要把握尺度,避免過度細分功能函數。
功能單一后,復雜度降低,自然可讀性增強
擴展性增強,針對某一個功能更加容易變更
變更引起的風險降低,由于功能單一,相互獨立,因此功能更改基本不會影響其他功能
從字面上理解不難。但是“看懂”和“會用”是兩回事,而“用好”更是難上加難。從工作經歷來看,很多朋友因為對這些原則理解得不夠透徹,導致在使用的時候過于教條主義,拿原則當真理,生搬硬套,適得其反。
如何理解?
一個類或模塊只負責完成一個職責或者功能。不要設計大而全的類或模塊,要設計粒度小、功能單一的類或模塊。但是,如果拆分得過細,實際上會適得其反,反倒會降低內聚性,也會影響代碼的可維護性。單一職責原則是為了實現代碼高內聚、低耦合,提高代碼的復用性、可讀性、可維護性
如何判斷類的職責是否足夠單一?
在不同的應用場景、不同階段的需求背景、不同的業務層面,對同一個類的或模塊職責是否單一,可能會有不同的判定結果。實際上,一些側面的判斷指標更具有指導意義和可執行性,比如,出現下面這些情況就有可能說明這類或模塊的設計不滿足單一職責原則:
類(模塊)中的代碼行數、函數或者變量過多(關聯小);
類(模塊)依賴的其他類(模塊)過多,或者依賴類(模塊)的其他類(模塊)過多;
函數過多(可以再具體分類);
比較難給類(模塊)起一個合適的名字;