代码质量在「内卷时代」的重要性
這里是Z哥的個人公眾號
每周五11:45 按時送達
當然了,也會時不時加個餐~
我的第「173」篇原創敬上
大家好,我是Z哥。
提到代碼質量,不知道你的腦海中浮現出的第一個詞是什么?規范?可讀性?優雅?
在我的心中,好的代碼質量 = 舒服。看著舒服,接手這樣的項目感覺舒服,在其中找問題和改代碼舒服。
軟件開發這個行業是一個年輕的行業,如果在十幾年前談代碼質量,可能還算是個比較高級的問題。但是在大家都認為越來越內卷的當下,注重和提升代碼質量我認為是每個程序員的必修課。說的嚴重一些,它影響了你在團隊中的價值,說的表面一些,它是你在團隊中的“面子”。
假象一下,你接手了兩個項目,一個項目代碼干干凈凈、非常整潔,另一個隨處可見的相似代碼以及雜亂無章的分類擺放。你對這兩個項目的前負責人是什么想法?未來你更愿意和誰合作?我想答案是顯而易見的。
在我看來要寫出舒服的代碼并不需要對那些代碼規范背的滾瓜爛熟,其實只要掌握一個六字核心原則:高內聚低耦合。如此寫出的代碼至少能在60分以上。
可以回想一下,當你在做一個簡單項目的時候,使用那些成熟的框架和工具可以輕松地完成大部分工作。甚至會感覺有點無聊,因為感覺都是在CRUD。這就是應用了經過精心設計的高內聚低耦合的框架和工具所具有的效果,讓事情變簡單。
講句題外話,「高內聚低耦合」在軟件開發領域真是一個黃金原則,在哪都適用,大到一個分布式系統的設計,小到一個class的設計。如果我的腦子只能記住一條原則的話,毫不猶豫會選擇它。
那么如何讓自己也能寫出高內聚低耦合的代碼呢?我們要對「高內聚低耦合」有更深入地理解,而不是僅僅停留在這6個字上。
葡萄牙馬德拉大學精確科學與工程中心的教授,被認為是計算機領域先驅者之一的賴瑞·康斯坦丁帶隊對內聚性和耦合性做了深入的研究和分析,對內聚性和耦合性的強弱關系進行了梳理,得到了以下結論。(摘自于維基百科)
內聚性的分類如下,強度由低到高排列:
偶然內聚性:是指模塊中的機能只是剛好放在一起,模塊中各機能之間唯一的關系是其位在同一個模塊中(例如:“工具”模塊)。
邏輯內聚性:是只要機能只要在邏輯上分為同一類,不論各機能的本質是否有很大差異,就將這些機能放在同一模塊中(例如將所有的鼠標和鍵盤都放在輸入處理副程序中)。模塊內執行幾個邏輯上相似的功能,通過參數確定該模塊完成哪一個功能。
時間性內聚性:是指將相近時間點運行的程序,放在同一個模塊中(例如在捕捉到一個異常后調用一函數,在函數中關閉已開啟的文件、產生錯誤日志、并告知用戶)。
程序內聚性:是指依一組會依照固定順序運行的程序放在同一個模塊中(例如一個函數檢查文件的權限,之后開啟文件)。
聯系內聚性/信息內聚/通信內聚:是指模塊中的機能因為處理相同的資料或者指各處理使用相同的輸入數據或者產生相同的輸出數據,因此放在同一個模塊中(例如一個模塊中的許多機能都訪問同一個記錄)。
依序內聚性/順序內聚:是指模塊中的各機能彼此的輸入及輸出資料相關,一模塊的輸出資料是另一個模塊的輸入,類似工廠的生產線(例如一個模塊先讀取文件中的資料,之后再處理資料)。
功能內聚性:是指模塊中的各機能是因為它們都對模塊中單一明確定義的任務有貢獻(例如XML字符串的詞法分析)。
耦合性的分類如下,強度由高到低排列:
內容耦合:也稱為病態耦合當一個模塊直接使用另一個模塊的內部數據,或通過非正常入口而轉入另一個模塊內部。
共享耦合/公共耦合:也稱為全局耦合指通過一個公共數據環境相互作用的那些模塊間的耦合。公共耦合的復雜程度隨耦合模塊的個數增加而增加。
外部耦合:發生在二個模塊共享一個外加的資料格式、通信協議或是設備界面,基本上和模塊和外部工具及設備的溝通有關。
控制耦合:指一個模塊調用另一個模塊時,傳遞的是控制變量(如開關、標志等),被調模塊通過該控制變量的值有選擇地執行塊內某一功能;
特征耦合/標記耦合:也稱為數據結構耦合,是指幾個模塊共享一個復雜的數據結構,如高級語言中的數組名、記錄名、文件名等這些名字即標記,其實傳遞的是這個數據結構的地址;
資料耦合/數據耦合:是指模塊借由傳入值共享資料,每一個資料都是最基本的資料,而且只分享這些資料(例如傳遞一個整數給計算平方根的函數)。
消息耦合:可以借由以下二個方式達成:狀態的去中心化(例如在對象中),組件間利用傳入值或消息傳遞 (計算機科學)來通信。
無耦合:模塊完全不和其他模塊交換信息。
如果你代碼寫的還不夠多,上面有些差異還無法很好的感知。但是你不需要把這些概念一字一句背下來,只要平時在寫代碼的時候多思考一下:“當前的代碼設計是屬于哪種類型?”。如果不能確定的話回頭來看這篇文章:D。慢慢地,通過寫更多的代碼,你會對耦合和內聚的強弱,有更敏感的感知力。
根據上面的這些概念,寫出高質量代碼的思路就很清晰了。method的歸類、class的歸類能根據功能內聚性歸類的絕不用順序內聚,能根據順序內聚性歸類的絕不用更弱的。耦合也是同樣的,能不耦合的就不耦合,能用消息耦合的絕不用數據耦合。
但是想要保證代碼按照這個設想去發展,還是需要通過做一些具體的事情作為抓手。這些事不需要全部做,但每一項都有助于提高代碼質量。
/01? 執行代碼規范+Code Review/
在Z哥看來,執行代碼規范,最重要的價值并不是非得讓100%的代碼符合這個規范,而是讓所有人一起養成一種意識,意識到我的代碼會被別人看到,被評價。這樣才能在寫代碼的時候,不僅僅是為了實現功能。
所以具體代碼規范是什么樣,并沒那么重要,可以是自己定義的,也可以是參考大廠的。當然我更推薦前者,大廠的規范雖好,但是你要全部照搬,這個執行成本可不小。
如果你想提高代碼質量,但又不想做很多事。那么執行代碼規范+Code Review可以是你的唯一選擇。如果你是一位個人開發者,那么可以讓身邊你認為代碼寫的最好的人幫你做CodeReview,以他的規范作為代碼規范即可。
很多人覺得代碼規范是一種約束,會降低開發效率。其實不會,最多在初期因為自己并不習慣一些規范,所以花了很多時間在修正代碼。一旦走上正軌后,代碼規范反而會提高開發效率,因為節省了很多閱讀代碼的時間以及同事之間溝通的時間。
就算它真的降低了開發效率,但你要提升效率也不應該降低代碼質量,而是通過其它方式去提效。
/02? 寫單元測試/
寫單元測試之所以能提高代碼質量,是因為如果不是高內聚低耦合的代碼,你會發現單元測試非常難寫。
比如,你只想測一下方法A,但是發現里面的依賴錯綜復雜,好吧,都stub掉。最后發現測一個方法寫了幾十個stub,這種操作我親眼看到過……。這就是前面提到的「內容耦合」過多了。
所以,能輕松地寫出單元測試,并且將其養成一種習慣,你的代碼質量必然不會差。
/03? 設計先行/
雖然設計不出完美的代碼,但是優先考慮設計可以讓你多思考“我應該怎么寫這段代碼”,而不是直接抄起家伙就寫,寫到哪算到哪。
畢竟大多數功能都不可能一步到位,需要多次迭代。這種情況下最初的設計就顯得尤為重要,畢竟大部分人遇到不舒服的代碼不會推翻重寫,最多就是修修補補,甚至是直接在這之上疊加新的代碼。
/04? 項目與團隊”微服務化”/
保證一個幾萬行代碼的項目質量和幾百萬行代碼的項目必然難度不同。所以,如果合適的話可以將項目拆小,并且由專門的團隊負責。這樣可以提高團隊把控代碼質量意愿,并降低其難度。
/05? 利用相關的工具/
主流的編程語言或多或少都有一些靜態代碼分析工具、單元測試覆蓋率統計工具,這些要充分利用起來。它們可以快速的幫助避免一些低級的代碼壞味道,節約大量時間。
/06? 幾個代碼層面的小建議/??
01? 勿過度使用鏈式編程
很多人會追求極致少的代碼行數,恰好鏈式編程能投其所好。的確在很多時候鏈式編程可以提高代碼的可讀性,但是它帶來的弊端也是顯而易見的,
調試的時候觀察變量變得很不方便。
容易在當前方法里處理不應該在這里處理的業務邏輯。畢竟很多class的方法和屬性是public的,相比單獨做一層封裝再調用,“點”出來直接用多香啊~所以在使用鏈式編程的時候也得遵守「得墨忒耳定律」。
得墨忒耳定律:
每個單元對于其他的單元只能擁有有限的知識:只是與當前單元緊密聯系的單元;
每個單元只能和它的朋友交談:不能和陌生單元交談;
只和自己直接的朋友交談。
維基百科
02? 避免隨處可見的try-catch
Try-catch雖好,切勿貪杯。很多人喜歡寫try-catch然后通過一個單獨封裝的通用返回模型告知調用方出現了什么異常。
這種方法的目的最初是為了避免上層調用者沒有做異常捕獲導致程序崩潰,但是弊端也是顯而易見的,如果調用方沒有正確的判斷返回模型里的異常相關屬性,會導致程序在錯誤的狀態下繼續執行,這個后果就不可預知了。
所以我認為通過try-catch封裝異常應該出現在更上層的代碼里,越底層的代碼越不應該封裝異常。
03? 認真編寫訪問修飾符
很多編程語言都有多個訪問修飾符,我們在編寫的代碼的時候應該盡可能的選擇最嚴格的修飾符,而不是什么都是public。
因為public會導致很多變量在不知道什么情況下就被外部修改了,導致bug層出不窮、排查困難,項目質量堪憂。
訪問修飾符的過于寬松也是前面提到的鏈式編程被過度使用的推手之一。
訪問修飾符的目的是為了防止程序員在無意間誤用不應該使用的方法和屬性,畢竟代碼往往不只有一個人寫。
04? 慎用繼承
繼承的確挺香的,可以少寫很多代碼。但是使用不當會破壞封裝的效果,造成訪問修飾符的失效。
繼承的正確使用姿勢應該傳達的是“子父”的關系,而不是“相似”的關系。比如“汽車”可以繼承于“交通工具”,但是不應該繼承于“自行車”,雖然它們都有輪子。
像汽車和自行車的這種情況要復用的話,可以抽象提煉出相同的部分,然后通過「組合」的方式進行。
最后,如果你對代碼質量有更高的追求,想修煉和強化“內功”,那必須不能錯過這本經典書籍。(之前的黃皮版本更新成這本灰皮了)
好了,總結一下。
這篇呢,Z哥和你分享了我對代碼質量這件事的看法。在行業越來越內卷的趨勢下,注重“質”總是沒錯的。
Z哥認為想要提高代碼質量最核心的原則就是:高內聚低耦合。文中給你羅列了賴瑞·康斯坦丁教授提煉了不同的內聚性和耦合性原則來表達關系的強弱。
基于對內聚性和耦合性原則的理解,再通過以下抓手進行代碼質量的提升工作:
執行代碼規范+Code Review
寫單元測試
設計先行
項目與團隊”微服務化”
利用相關的工具
最后還分享了幾個代碼層面的建議:
勿過度使用鏈式編程
避免隨處可見的try-catch
認真編寫訪問修飾符
慎用繼承
希望對你有所啟發。
推薦閱讀:
及時享樂,延遲痛苦
原創不易,如果你覺得這篇文章還不錯,就「在看」或者「分享」一下吧。鼓勵我的創作 :)
如果你有關于軟件架構、分布式系統、產品、運營的困惑
可以試試點擊「閱讀原文」
總結
以上是生活随笔為你收集整理的代码质量在「内卷时代」的重要性的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 对 Redis 中的有序集合Sorted
- 下一篇: .NET Core AWS S3云存储