寫出結構優雅代碼的4個技巧

轉載自: https://mp.weixin.qq.com/s?__biz=MzUzNjAxODg4MQ==&mid=2247487003&idx=1&sn=7677c62fbc2acddabc082fcea93a434e&chksm=fafde4b5cd8a6da3c91ea9918188ff968cf2bfab92a8e697fee3298a05bd5a30ba286038b216&token=693383320&lang=zh_CN#rd

 

寫出結構優雅代碼就像成爲武林高手一樣,需要積累思考勤學苦練。冰凍三尺非一日之寒。成熟的業界套路肯定是沒有的,因爲代碼也有思想流派。現在是百家齊放的時代。code review時,一個不留神就會吵起來。

 

我在網上找了一下資料基本都是《代碼整潔之道》、《重構:改善既有代碼的設計》和《代碼之美》這三本書裏的內容。建議還是自己看書。可以在【編程一生】公衆號裏回覆:666  獲取我的經典電子藏書,這三本書都在裏面。

 

下面有一些我自己的總結,前三個別的書中沒有。

 

使用切面將業務邏輯與其他方面解耦

 

最簡潔優雅的代碼就是隻做一件事的代碼。很多與業務無關的邏輯,如日誌、事務都可以使用切面。

 

爲了避免抽象,這裏詳細介紹怎麼用切面打印結構化日誌。不感興趣的可以直接跳過,不影響整篇理解。

 

結構化日誌,顧名思義不再是自由格式的日誌,而是遵循了一定的結構:每一行日誌遵循相同的結構。好處顯而易見:簡化日誌解析,使得日誌的後續處理、分析或查詢變得方便高效。一般用在需要集中採集到日誌服務器上,用來在監控中顯示和做數據分析。數倉領域有數據湖的概念,集中採用可以叫做數據入湖。

 

在分佈式環境下,一般都需要結構化日誌採集。一旦發生問題,從監控上至少要能定位問題出現在哪臺機器上,然後再去機器上查所有日誌。

 

因此,結構化日誌一般是方法的開頭和結尾都打印,或者只在結尾打印兩種方式。因此非常合適用切面處理。問題來了,方法執行結果怎樣切面怎麼知道呢?

 

具體代碼我之前有上傳github,《簡明日誌規範》裏也有相應的介紹,這裏只說重點。

 

首先,在Java等語言的切面中是可以拿到方法的返回結果的,如:

business = pjp.proceed();

business 就是返回結果。

 

其次,代碼中可以進行埋點。定義埋點可以使用面向對象的形式,向對象賦值即可。

圖片

 

但是打印日誌打印的最後不是字符串嗎?可以使用充血模型在日誌構造器中用反射定義對象轉格式化日誌的通用方法:

圖片

 

整個對象可以使用ThreadLocal保存在線程的生命週期中,切面可以從ThreadLocal中獲取數據執行打印。

圖片

 

代碼使用時需要特殊賦值的地方直接進行賦值,其他事情不需要關心。效果如下:

圖片

 

完整的代碼我擇期更新到github上,其實除了最後這個使用的地方,我寫的都是通用的,直接拿來用即可。看在女生節的份上,讓我犯犯懶,最近先不更新。其實我也不是真的懶,女生節要到了,有很多活動要推掉。上班時間不處理,週末了總得委婉的回覆一下。

 

PS:這種活動我都不參加,請各位編輯、運營小姐姐們不要找我了哈,咱不蹭熱度~~

 

寫有風格的代碼

 

在《代碼榮辱觀-以運用風格爲榮,以隨意編碼爲恥》中我有詳盡的說明和代碼講解,文章風格與代碼風格相得益彰,強烈推薦大家再讀一遍。

 

巧用語法糖

 

一個巧字,暗示着需要深入的理解。只有學的深,才能用的巧。在《深入理解函數式編程》中我有詳盡的說明和代碼講解,這也是自己滿意的文章之一,請大家參閱。

 

《代碼整潔之道》中提煉的技巧

 

書中詳細介紹,這裏做總結,有興趣可以自己看書。可在【編程一生】公衆號中留言:代碼整潔之道。就可以獲取完整電子書。

 

註釋

 

  • 不要給不好的名字加註釋,一個好的名字比好的註釋更重要

  • 不要“柺杖註釋”,好代碼 > 壞代碼 + 好註釋

  • 在文件/類級別使用全局註釋來解釋所有部分如何工作

  • 一定要給常量加註釋

  • 團隊統一定義標記

    • TODO  待處理的問題

    • FIXME  已知有問題的代碼

    • HACK 不得不採用的粗糙的解決方案

 

  • 在註釋中用精心挑選的輸入輸出例子進行說明

  • 註釋應該聲明代碼的高層次意圖,而非明顯的細節

  • 不要在代碼中加入代碼的著作信息,git可以乾的事情不要交給代碼

  • 源代碼中的html註釋是一種厭物, 增加閱讀難度

  • 註釋一定要描述離它最近的代碼

  • 註釋一定要與代碼對應

  • 公共api需要添加註釋,其它代碼謹慎使用註釋

  • 典型的爛註釋

    • 不恰當的信息

    • 廢棄的註釋

    • 冗餘註釋

    • 糟糕的註釋

    • 註釋掉的代碼

 

  • 唯一真正好的註釋是你想辦法不去寫的註釋

    • 不要有循規式註釋,比如setter/getter註釋

    • 不要添加日誌式註釋,比如修改時間等信息(git可以做的事情)

    • 註釋一定是表達代碼之外的東西,代碼可以包含的內容,註釋中一定不要出現

    • 如果有必要註釋,請註釋意圖(why),而不要去註釋實現(how),大家都會看代碼

    • 適當添加警示註釋

 

命名

 

  • 儘可能使用標準命名方法,比如設計模式,通用學術名詞等

  • 命名要找更有表現力的詞

    • 使用更專業的詞,比如不用get而使用fetch或者download

    • 避免空泛的名字,像tmp

    • 使用具體的名字來細緻的描述事物

    • 給變量名帶上重要的細節,比如加上單位ms等

    • 爲作用域大的名字採用更長的名字,作用域小的使用短名字

    • 變量類型爲布爾值表達加上is,has,can,should這樣的詞會更明確

 

  • 變量名稱長短應該與其作用域對應

  • 別害怕長名稱,長而具有描述性的名稱比短而令人費解的名稱好

  • 函數名稱應該說明副作用,名稱應該表達函數,變量或類的一切信息,請不要掩蓋副作用,比如CreateAndReturnXXX

     

方法

 

  • 函數不應該有100行那麼長,20行封頂最好

    • if else while等控制語句其中代碼塊應該只有一行,也就是一個函數調用語句

    • 函數的鎖進層次不應該多於兩層

    • 一個函數只做一件事,一個函數不應該能抽象出另外一個函數

 

  • 某個公共函數調用的私有函數緊隨其後

  • 最理想的參數是零參數,最長不要超過三個入參,儘量不要輸出參數

    • 如果函數傳入三個及以上參數最好將其抽象爲類

    • 標識參數十分醜陋,向函數傳入布爾值用於區分不同業務的做法很醜陋,應該拆分爲多個函數

 

  • 別返回null值,拋出異常或者返回特殊對象,儘量避免NPE

  • 別傳入null值

 

異常與錯誤

 

  • 抽離try catch包含的代碼塊,其中代碼塊抽象爲一個函數

  • 拋出的每個異常,都應當提供足夠的環境說明,已便判斷錯誤的來源與處所

  • 不要將系統錯誤歸咎於偶然事件

     

併發

 

  • 分離併發相關代碼與其它代碼

  • 嚴格限制對可能被共享的數據的訪問

  • 避免使用一個共享對象的多個同步方法

  • 保持同步區域微小,儘可能少設計臨界區

 

單元測試

 

  • 不要怕單元測試的方法名字太長或者繁瑣,測試函數的名稱就像註釋

  • 不要追求太高的測試覆蓋率,測試代碼前面90%通常比後面10%花的時間少

  • 使用最簡單的並且能夠完整運用代碼的測試輸入

  • 給測試函數取一個完整性的描述性名字,比如  Test _

  • 測試代碼與生產代碼一樣重要

  • 如果測試代碼不能保證整潔,你就會很快失去他們

  • 每個測試一個斷言,單個測試中斷言數量應該最小化也就是一個斷言

  • FIRST原則

    • 快速 Fast

    • 獨立 Independent  測試應該相互獨立

    • 可重複 Repeatable  測試應當在任何環境中重複通過

    • 自足驗證 Self-Validating   測試應該有布爾值輸出

    • 及時  Timely   最好的方式是TDD

 

代碼結構

 

  • 代碼行長度控制在100-120個字符

  • 可能用大多數爲200行,最長500行的單個文件構造出色的系統

  • 關係密切的代碼應該相互靠近

    • 變量聲明應該靠近其使用位置

    • 若某個函數調用了另外一個,應該把他們放在一起,而且調用者應該放在被調用者上面

    • 自上向下展示函數調用依賴順序

 

  • 應該把解釋條件意圖的函數抽離出來,儘可能將條件表達爲肯定形式

  • 不要繼承常量,比如接口中定義常量,不要使用繼承欺騙編程語言的作用範圍規則

  • 模塊不應瞭解它所操作對象的內部情況

  • DTO(Data Transfer Objects)是一個只有公共變量沒有函數的類

  • 對象暴露行爲,隱藏數據

  • 不要使用“尤達表示法” 如 if(null == obj),現代編譯器對if(obj = null)這樣的代碼會給出警告

  • 一般情況使用if else,簡單語句使用三目運算符

  • 通常來講提早返回可以減少嵌套並讓代碼整潔

 

設計

 

  • 類應該足夠短小

    • 類應該滿足單一權責原則(SRP),類和模塊只有一個修改理由

    • 類應該只有少量的實體變量

    • 類應該遵循依賴倒置原則 DIP(Dependency Inversion Principle),類應該依賴於抽象而不是依賴於具體細節

    • 類中的方法越少越好,函數知道的變量越少越好,類擁有的實體變量越少越好

 

  • 通過減少變量的數量和讓他們儘量“輕量級”來讓代碼更有可讀性

    • 減少變量

    • 縮小變量的作用域

    • 只寫一次的變量更好,如常量

 

  • 最好讀的代碼就是沒有代碼

    • 從項目中消除不必要的功能,不要過度設計

    • 從新考慮需求,解決版本最簡單的問題,只要能完成工作就行

    • 經常性地通讀標準庫的整個API,保持對他們的熟悉程度

 

  • 簡單設計

    • 運行所有測試

    • 不可重複

    • 表達了程序員的意圖

    • 儘可能減少類和方法的數量

    • 以上規則按重要程度排列

 

  • 無論是設計系統或者單獨模塊,別忘了使用大概可工作的最簡單方案

  • 整潔的代碼只提供一種而非多種做一件事的途徑,他只有儘量少的依賴。明確定義並提供儘量少的API

  • 減少重複代碼,提高表達力,提早構建,簡單抽象

 

參考:
https://www.zhihu.com/question/28492982/answer/448474779


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章