如何編寫簡潔代碼?(下)
作者 | 袁慎建
接上篇《??如何編寫簡潔代碼?(上)??》
代碼不講真話的直接后果是所有人被誤導了,然后做了一件錯誤的事情,不自知地將錯就錯,讓錯誤越陷越深,最后浪費寶貴的時間。可不講真話,編寫代碼的人又不是故意的,也萬萬不可上綱上線,袁帥秉著內訓師作為知識沉淀者和文化傳播者角色的原則,借助教育代碼的機會組織了一次部門內部的閑聊會,并美其名曰:Clean Code交流會。

講真話了嗎?
周五,是一個心情放松的日子,距離年會過去也快整整一周了。袁帥也趁此機會召集了團隊的幾名開發在線學習系統的小伙伴來碼聊。看起來是一次不經意的安排,其實他早有準備。
偽修飾
會議室里,袁帥開門見山:“最近跟一個北美 BP 在協商給他們大客戶團隊的骨干成員強化敏捷工程實踐,現在很多部門內訓有一套敏捷工程實踐課程。但是很有意思的是,這個BP跟我線上會議溝通時,從來不一步到位,總會在微信上再跟我發文字再解釋一遍,也不知道是我看著傻,還是她認為我癡呆。”
大家被袁帥這開場給逗樂了,拋給他好奇的目光。
“沒講清楚唄,再給你講一遍。”清揚一副若無其事的樣子。
“嗯,她擔怕開會沒講清楚,想再跟我解釋一遍,而且每次微信上的留言比她會議上講的要更明確更具體。所以,后來慢慢地我習慣在會議上忽視她提供的信息,直接看她的微信留言。”
“那你們還干嘛開會呀,不是白費時間嗎!” 萬正義直率地補充道。“可是,有時候她在后續的會議上又把之前留言的內容給更新了卻忘了再給我留言。” 袁帥表現得無辜。
“所以,你仍然按照微信留言的信息,結果誤解了真實的意思!” 清揚有搶答道。
“嗐,你還別提,我最近都沒跟活人打交道,也總是遇到這種煩惱。” 劉歡歡一臉滿腹牢騷的表情。
“是嘛,說來聽聽?”袁帥故作驚訝般試探道。
“注釋啊,最近看到一些代碼的注釋,本來不看注釋花點時間琢磨琢磨代碼,還能搞清楚代碼在做什么,可是有時想走捷徑,瞅了眼注釋,發現注釋是錯的,騙的我團團轉,被謊言帶節奏真不爽!” 歡歡打趣地補充。
“對的,這就跟我提到的那個BP類似,總習慣加一些修飾,本來初心是好的,是想解釋得更清楚,可是修飾只是一種補充信息,有時候原始信息變更了,修飾卻沒有跟著更新,不但沒有起到修飾作用,反而會誤導別人,很耽誤事兒!” 袁帥同情地回復了正義。
“說白了,還是沒有想清楚,代碼要做什么事情,然后覺得需要通過注釋去解釋一下,后面代碼改了,大概率是不會修改注釋的。” 正義總是話里充滿著正義。
“可能是使用中文注釋讀起來更順暢吧,因為代碼不太好用中文命名”。程曉娜以理解的口吻弱弱地補充了一句。
袁帥突然陷入了沉思,腦海里浮現出兩個畫面:
- 他曾經輔導過的某些程序員,因為英語功底不好,很難將中文翻譯成恰當的英文,這在一定程度限制了他們,即便有翻譯工具的輔助。
- 有一次為了給一個方法起名字,他跟三個10多年的工作經驗的技術Leader一起討論了10來分鐘,最后才搞定,但大家很開心。
“為什么要寫注釋啊?代碼自解釋不香嗎?只有代碼沒法自解釋的時候才用一下注釋還能接受!” 正義越發正義起來。
“可有時方法太長了,做的事情比較多,每一段相關的代碼用注釋說明一下在做什么也不是不可取的吧?”清揚見縫插針地拋了個問題。
“Talk is cheap,show me the code!”袁帥展示了準備好的代碼:

“2分鐘時間,大家先仔細閱讀這幾段段代碼,把你看到的問題和建議改進措施發到咱們的討論群里哈。”
5分鐘后,袁帥把所有人的答案匯總起來:
- 示例1:類上的注釋完全沒必要,因為VCS工具能夠很好地做記錄。
- 示例1:構造方法方法上的注釋是冗余的,構造器本身就能表達構造對象,參數也能表達傳入的東西。
示例2:方法的注釋的確起到了解釋作用,但是讓方法自解釋更香:更名為listByStatus后干掉注釋。
緊接著,袁帥再拋出一個復雜的示例:

大家花了3分鐘找出問題:
- 示例3:方法太長,中間分段注釋解釋做什么的可以抽取方法,然后將注釋轉換成方法名來表達意圖。
- 示例3:printReceipt方法內打印日期和客戶忠誠度注釋過期了,實際上代碼被注釋掉了。
- 示例3:printReceipt方法內那個稅率寫的是15%,而實際用的10%,誤導讀者。
“注釋并不是一無是處,畢竟那誰講過 -- 「存在即合理」。注釋存在這么多年,而且很多地方咱們也看到過。” 袁帥又恢復了主持人的狀態。
“有啊,「開放API文檔」到處都是API注釋,而且還要寫好看了。”
“最近有幾段代碼實在有些實現的難言之隱,我必須通過注釋來解釋一下。”
“嗯,我剛碰到過「法律版本信息」的一些注釋,白紙黑字的注釋聲明不能少。”
“魔術代碼可以注釋一下啊,比如復雜的郵件正則表達式:
”
袁帥對最后這一個補充有點感興趣,趁機提了個問題:“魔術代碼除了注釋,還有更好的方式嗎?”
“引入「解釋性變量」啊,比如:emailMatcher。”清揚這次臨場發揮反應很快。
“大家怎么看待「TODO | FIXME」這種注釋呢?”清揚緊接著有拋了個問題。
“不提倡,雖然它能幫助我們在本地記錄一些代辦列表,但盡可能及時處理完這些任務,將注釋清理掉,別提交到生產上。”正義的聲音覆蓋了全場。
袁帥朝清揚和正義點了個大拇指贊,簡單做了個總結:“有一些場景需要注釋的,當我們想要添加注釋的時候,我們首先應該想想代碼能否做到自解釋?”
在收拾電腦之際,他腦海里浮現出Uncle Bob關于注釋的一句話:
“注釋不同于《辛德勒的名單》。它們不是‘純善的’。事實上,注釋充其量是一種必要的惡。”
-- Robert C.Martin
行外話
老馬(Martin Fowler)的博客上有一句話:“There are only two hard things in Computer Science: cache invalidation and naming things.”。這句話不是老馬本人講的,老馬表示很認同,袁帥也很認同,而且越來越信。
距離上一次的 Clean Code 交流會過去剛好一周,今天天氣格外晴朗,清揚饒有興致喊上袁帥去樓底下新開的小餐館炒倆菜。
兩人來到餐廳剛坐定,袁帥手機微信語音響起來,是隔壁團隊的 TL 石彪,想約他聊聊上次TDD工作坊后大家的落地情況和疑惑。
清揚伸手遞過菜單示意袁帥點個菜,他只說了個“青菜”繼續跟石彪聊了起來。清揚找了好一會也沒找到“青菜”,見袁帥聊得投入,就幫他點了個綠色的白灼生菜,還點了只清蒸桂花魚,外加兩份單人份的土雞湯和米飯。
10分鐘后,“青菜” 上來了,袁帥吃了兩口覺著不對勁:“咦,我點的是青菜啊,怎么上了個生菜呢?”
“大哥,我盡力了,菜單沒有找到“青菜”,就點了個這咯。” 清揚一臉無辜。
“哦,我們那有一種叫“上海青”的蔬菜,平時我們都叫青菜。” 袁帥說完立即意識到自己現在身在北方,這邊的叫法跟老家可能不一樣。
“哈哈,我們西安把青菜理解成綠色的蔬菜~” 清揚說著就遞過來菜單。
看到 “清炒油菜”,袁帥心里開始琢磨著:“不同地區(上下文),對同一個東西的叫法是可能不一樣的,如果切換了地區,自己還沿用原來地區的叫法,很可能造成困惑和誤解。”
“不同行業,不同領域,不同上下文,同物可不同名。你常常給我講寫代碼的時候要特別注意這一點,開發哪個行業的系統,就應該使用該行業的業務語言,有利于統一語言,交流起來效率會高很多,而且代碼跟業務相匹配,更容易理解。” 清揚猜到了袁帥在琢磨什么,替他做了一個總結。
“你們的清蒸桂花魚,請慢用!” 服務員把香氣四溢的魚端到桌上,這是袁帥小時候特別喜歡的一道菜,他開心地拿起筷子正要去夾魚尾:“咦,剛才服務員叫它清蒸桂花魚?” “對啊,桂花魚,清蒸的,營養健康,色香味俱全。”清揚麻利地回應著。
袁帥拿起菜單看了一眼,嘴里邊嘟囔邊若有所悟地點頭著:“在我老家這個菜叫「清蒸鱖魚」。”
清揚瞥了他一眼,作搖頭嘆息狀伸手去夾魚,開心地吃了起來。“小鬼厲害啊,竟然點了我最喜歡的魚,這頓飯我請了哈~” 袁帥這次快速從他的菜品命名的思緒中跳脫出來。
吃完飯回來,袁帥喊上清揚去看看隔壁石彪的團隊在做的Code Review,見到大屏幕上的代碼:

“小豹,這個FlyLine是指飛行路線嗎?” 石彪小心翼翼地問。
“嗯,是這個意思!”
“你在IDE搜一下【Route】” 只見小豹比較嫻熟地用快捷鍵定位到一個Route類:

看到這個類,小豹快速在正開著的Google翻譯框里查到【Route:航線】。
“你打開Trello卡上的【航空術語】那張卡片上的有個航空術語鏈接。你打開頁面敲關鍵字【航線】進行搜索。”石彪見小豹下意識撓著頭不知所措,就進一步給了提示。
石彪10秒鐘的指尖操作后,大屏幕上將頁面展示出來:

“小豹剛來,對這些航空行業術語不熟悉可以理解,我提前給大家分享一條我的經驗:入行說行話,既然咱們在開發航空系統,我們就要講航空行話。” 石彪刻意掃視了所有在場的小伙伴,繼續說道:“那怎么學會這些行話呢,翻譯工具是一個好助手,但有時它也會不靈,咱們團隊一直有在維護了一套專門的術語,上周我剛把它開發成Web版部署在內網服務器上了,咱們隨時可以在上面檢索術語哈。”
石彪說完,小豹立刻示意結對的小伙伴在卡片上記下了一個Action:[ 更換Flight類中引用Flyline --> Route ]。
小結
如果說「域冗余」、「碼尾禪」、「層錯綜」這些是代碼在溝通表達時的 偏形,那「偽修飾」、「行外話」這些就屬 走神 了,而這些貌合神離的假話更容易蠱惑人心。
寫代碼本沒什么大事,努力打磨這五點,時刻審視代碼,時刻自省,軟件的可理解性可提升至少90%,沒錯就是很高。

























