源碼即設計:程序員也是時尚大師

      網友投稿 750 2025-03-31

      我們經常談論架構,討論設計,卻甚少關注實現和代碼本身,架構和設計固然重要,但要說代碼本身不重要,我不同意,Robert C.Martin大叔也不同意,Martin認為“源碼即設計”。

      在討論具體的實施細則之前,我們不妨討論一下什么是好代碼?蘿卜特 C.Martin認為:衡量代碼質量的唯一標準是:WTF/min,也就是review代碼的時候每分鐘說“握草”的次數。這個定義雖有辱斯文,但粗野中不失奔放,調皮中又蘊含哲理。

      好的代碼如同文筆優美的散文,行云流水,賞心悅目,閱讀的時候,如沐春風,帶給人愉悅與啟迪。

      好的代碼猶如構思精巧的小說,它或許不夠平鋪直述,卻足夠引人入勝,讀到最后,你會豁然開朗,我去,原來是這樣的啊,那一刻,你會覺得過程中的曲折和探索都是值得的。

      而壞的代碼,猶如病毒,它不僅癱瘓你的程序,還有很強的傳播效應,等到它擴散開來,神仙難治。

      壞的代碼,像一個泥團,閱讀的時候,你仿佛被困于黑暗的迷宮,又仿佛在跟一個絮絮叨叨的人交談,她的腦回路經常短路,說話含混不清,主次不分,叨逼半天,你依然get不到她的中心思想,你常常感覺智商受到了莫大的侮辱,你面露艱難神色,心中萬馬奔騰。

      有很多區分好壞代碼的規則,我也看過一些,對于文章中提到的一些標準做法,就不重復嚼舌頭根子了,我想結合自己的工作經歷,談一談自己的切身體會。

      閑扯半日,言歸正傳,要編寫彌漫好味道的代碼,要遵循哪些約束和指引呢?

      一致性

      持之以恒的遵從一致性規則,在代碼風格上,爭論個三天三夜估計也定不出個好壞出來,但好的風格一定是強一致性的,這一點應該比較容易達成一致吧?風格的好壞其實更多受習慣的影響,頭發少一點的程序員應該都有自己風格變遷的經歷,多年前自己篤信不疑的good style或許正是當前深惡痛絕的bad style,所以我主張在style上擱置嘴炮,一個項目應該有一個編碼規則,好的規則應該是以理服人的,好的規則應該是拒絕任性夾帶私貨的,規則定了之后,就遵照執行吧,可能某個風格跟你不相符,但沒關系,你要知道,這并不意味,你在style之戰敗下陣來,也并不表示它說服了你,你遵守的是規則和紀律本身。

      變量(包括文件、類/結構體、函數)命名,比如ohmygod,你可能搞不清哪些字母是一伙的,所以需要界定單詞。駝峰通過單詞首字母大寫來界定單詞,另一個慣用做法是用下劃線拼接單詞。駝峰的弊端是丑,下劃線拼接的弊端是增加了標識符長度(相比首字母大寫),好處是跟std c/c++、linux kernel的做法一致,喜歡kernel的碼農容易找到如家般的歸屬感。

      c++有namespace避免沖突,c經常用prefix防止命名污染全局空間,但我認為命名簡潔扼要很重要,所以我支持簡短的前綴而反對冗長的前綴。

      代碼密度

      實現同樣的功能,你喜歡100行代碼,還是20行代碼?如果貴leader不以代碼行數考核績效我建議你把代碼寫的精簡,而如果貴leader以代碼行數考核績效,我建議你轉行,開滴滴,送外賣或者擺攤都行,因為在這樣的leader下面耗費青春基本上也不會有什么發展前途。

      把簡單的東西搞復雜化很容易,你只需要找一個能力平庸的人就能實現化簡為繁的愿望,而化繁為簡則堪稱化腐朽為神奇。也許你要說,我欠缺簡化的能力,這并不奇怪,坦白講,這不是一件容易的事,你做不到沒關系,但你擁有正確的理念更重要,它將幫助你認清前進的方向,而不是在錯誤的道路上越走越遠。

      有些項目,充斥各種無效代碼,其實你只需要稍加思考,你就能識別出來。

      比如大塊注釋掉的代碼像發臭的尸體一樣遍布其中。比如大量功能重復的代碼像垃圾一樣堆砌在那里。比如本不需要返回值的函數恒定的返回true。

      又或者函數一進來,不管三七二十一,對入參一頓檢查,全然忘記你在編寫的是一個私有實現函數,你在調用它之前已經檢查過一遍,私有函數是一個受控的安全上下文,這不僅不優雅而且不綠色(低效耗電)并且不安全(在該崩的時候沒崩把雷埋到了更隱蔽的地方),話說你看標準庫函數strcpy/strcat,vector operator[]檢查傳參了嗎?

      提高代碼密度或者說濃度有利于理清思路,有利于突出重點,有利于提高維護性,而充斥各種無效語句的代碼只會把關鍵語句淹沒在汪洋大海,使得review代碼的人get不到重點,看不清主次。像聽一個絮絮叨叨的人做報告,滿篇廢話,像看一個劇情拖沓的連續劇,昏昏欲睡,像喝一瓶二鍋頭兌十斤白開水,口能淡出個鳥來。

      重構是程序員的口頭禪,重構是在保持程序功能不變的情況下調整架構和實現,我認為提高代碼密度應作為重構的一項追求。

      linux kernel、lua、nginx、skynet這些優秀的開源庫代碼濃度都很高,建議讀者朋友品嘗一下。

      封裝

      源碼即設計:程序員也是時尚大師

      我們最常干的一件事就是把重復編寫的代碼封裝到一個函數里去,用多處調用替代重復編寫,這個很好理解,但其實即使不被多處調用,把相關的一段代碼封裝到一個實現函數也是有必要的,因為把代碼平鋪開來,把細節暴露出來,容易掩蓋重要的東西,即框架和脈絡會變得不夠清晰。

      一個見名知義的函數調用比堆砌在那里的一段代碼給我的感受好,我如果關心它是怎么做的,我可以跳轉到定義看看實現。

      封裝的一個核心原則是單一職責,符合單一職責的函數更易于被復用。

      避免特例

      linus大神分享過他心中的好代碼,說的是針對鏈表的操作,他更喜歡統一性的處理方式,而不是做特例化的處理,我想這個例子很有代表性,它其實代表一種理念,那就是自始至終,我們的頭腦里必須優先考慮normal化的處理方式,當然這其實是一個比較高層次的要求,菜鳥互啄可以先跳過這一層要求。

      縮寫

      慎用縮寫,相比縮寫帶來的含混不清,我寧愿多敲幾下鍵盤,如果要縮寫請符合慣例遵從常規,比如AI,比如App,比如cfg,但是你把threshold縮寫成threshod,把Item縮寫成Iem,我特木真的搞不懂這是拼錯了還是縮歪了?

      解耦

      構建松散耦合的系統一直是軟件工程的一個目標,模塊化的一個方向便是解耦,但我們口口聲稱心心念想的解耦,在實施層面又有幾分體現呢?

      比如,我經常干的一件事就是把類似配置文件,或者宏定義的東西集中的一個頭文件里去,看起來很統一也很正規,起碼我之前也是這樣認為的,但忽然有一天,發現自己這樣做顯得很不聰明的樣子,為什么呢?因為你想把所有模塊配置相關的東西都塞進配置公共文件真的合適嗎?是不是把公共接口抽離出來更好,把配置相關的數據下沉到各模塊更合適?

      另外,把宏都定義到一起,這意味隨便改點東西,都會需要修改宏頭文件,而這個頭文件就會成為程序世界的中心,修改公共宏文件幾乎會引起整個系統的所有源文件rebuild,這簡直就是AOE團滅啊。所以更好的方式是分而治之,去集中式。

      我們知道c/c++的編譯單元是source file(.c/.cpp),編譯的第一步是預處理,所有include都會展開替換,所以我們要避免引入任何不必要的頭文件,也應該把本編譯單元用到的頭文件都include進來,這就是所謂的頭文件自給自足。這點很重要,但很多人會不以為然,甚至有些人會自作聰明的搞一個allincluded.h,把常用的一些頭文件全部include進來,然后自認為一勞永逸的完美的解決了問題,包含不必要的頭文件會增加編譯時間,會增加依賴,我們不僅應該避免錯誤的包含,還應該精心設計和劃分文件,使得每個文件的功能足夠內聚單一。

      遵從標準

      我遇到每個模塊單獨定義自己的各種原生(build-in)數據類型,但我建議不要這樣做。如果你只是需要解決不同體系結構下long等整型的長度差異,我想告訴你,c庫頭文件stdint.h已經從標準層面統一解決了這個問題,里面int8_t/16_t/32_t/64_t,還有uint8_t等等應有盡有。

      宏是c的一個有效武器,在有些情況下確實行之有效,關于宏,我是騎墻派,我既反對禁用宏,也反對濫用宏,inline可以部分替代宏,但不能完全替代宏。

      如果項目里到處都是宏,全大寫,至少1/3的代碼都是各種詭異的宏,你review代碼的時候,不停的跳來跳去,看了一眼,哦,就這樣啊,然后切回來,頻繁的上下文切換是低效的,它打斷了你的思路,其實很多時候完全沒有必要。

      命名

      命名有一些指引,比如類/結構體應該用名詞,函數應該用類似動詞或者doSomething這樣的動賓結構,這些規矩都是耳熟能詳的。

      我主張命名應該簡明扼要,不要羅里吧嗦,要準確的表達出它要做的事情,如果你碰到命名困難,你可能需要考慮你的類定義或者接口劃分是否合適。

      命名是接口的一部分,很重要,好的命名是自注釋的。

      我反對匈牙利命名法,理由:不能一致性的解釋各種類型,把類型編碼進變量不合理,變量名本身就能體現它的類型,無法適用template情況,始作俑者ms放棄了它。

      如果你沒有思路,那我建議你參考一下STD C/C++ API,畢竟這些接口歷經幾十年沒有大的變化,算是經受住了歷史的考驗,比如malloc/free/atoi,stl 容器的成員函數也有點意思:size()、 capacity()、resize()、reserve()、push()、pop()、top()、back(),很干脆,不廢話,我覺得很好。

      所以,如果你編寫的是某某管理器,比如ItemManager,我建議你直接取名add(),remove(),而不用AddItem(),RemoveItem(),因為你本身就是Item的Manager,操作的必然是Item,而且從參數上也能體現出來,少即是多,多不如少。

      擴展性

      開閉原則是應對擴展性的rule,人無遠慮必有近憂,說的是我們不能局限于眼前,但也請不要過度設計,不可盲目迷信擴展性,戲太多也是病。

      知乎有一篇神貼講的是如何把helloworld搞成一個big project,當你想給別人項目挑刺的時候,你可以用擴展性說事,但我建議你離開口閉口擴展性的人遠一點,據我觀察,這種人大多比較虛偽而且很水。

      高效而魯棒

      有很多避免運行低效的做法,比如哈希、減少拷貝、提高局部性、buffer/cache、空間時間置換、內聯、分支預測、判斷前置、計算延遲、無鎖編程。

      提高魯棒性的關鍵是保持簡單,任何引入復雜性的動作都需要保持足夠警惕。不信任/零信任設計,面向failed編程,假設依賴的上下文,上下游都是不可靠的,去中心化去關鍵路徑,熔斷,降級,避免驚群效應,方法很多,不一一列舉了。

      最后

      噴了這么多,也給大家一次噴我的機會,我貼出來自己的代碼,https://github.com/ZhuanJia/mynet,這是13年剛進廠時候周末自己搗鼓的玩具,它或許有這樣那樣的問題,如果你覺得不錯,請給我,如果你覺得很水,那我可以甩鍋給時間,畢竟7年前寫的嘛。橋水的CEO說過,如果你不覺得一年前的自己是沙雕,那說明你這一年沒什么進步。何況是七年前呢。

      搬磚不容易,累了,擱筆休息,擇日再議!

      軟件開發 程序員 架構設計 日志分析服務 LOG 代碼

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:2020省賽->棋盤放麥子
      下一篇:Excel中一鍵快速突出顯示所有命名范圍
      相關文章
      亚洲成电影在线观看青青| 亚洲一区二区三区首页| 久久国产亚洲精品无码| 好看的亚洲黄色经典| 亚洲综合国产一区二区三区| 精品亚洲一区二区三区在线播放| 亚洲AV无码乱码在线观看牲色| 亚洲Aⅴ在线无码播放毛片一线天| 亚洲国产系列一区二区三区| 亚洲中文字幕无码av在线| 亚洲熟妇无码爱v在线观看| 噜噜噜亚洲色成人网站∨| 亚洲高清无在码在线无弹窗| 亚洲福利电影在线观看| 亚洲精品免费在线| 亚洲校园春色小说| 亚洲国产精品综合久久久| 亚洲毛片一级带毛片基地| 亚洲午夜国产精品| 国产成人亚洲精品| 亚洲中文字幕无码中文字| 亚洲精品无码久久久久A片苍井空| 亚洲中文字幕久久精品无码VA| 亚洲偷自精品三十六区| 国产午夜亚洲精品| 亚洲AV无码之国产精品| 麻豆亚洲AV成人无码久久精品 | 在线精品亚洲一区二区| 久久精品国产亚洲AV久| 四虎必出精品亚洲高清| 亚洲youwu永久无码精品 | 好看的电影网站亚洲一区| 亚洲欧洲免费视频| 亚洲婷婷综合色高清在线| 亚洲国产91在线| 久久综合亚洲色hezyo| 国产成人久久精品亚洲小说| 久久久久亚洲AV无码专区网站 | 亚洲一区二区三区播放在线| 国产精品亚洲综合久久| jizzjizz亚洲日本少妇|