2012
Dec
25

如何寫一個大家都看得懂的程式呢? 最近讀了 「The Art of Readable Code 」與「Clean Code A Handbook of Agile Software Craftsmanshi」這本書,將書中提到的,整合我平常寫 Code 的習慣,記錄下程式的規範。

Easy to Read, 如何讓整體的 Code 容易閱讀

  • 複雜的 Function, method 請增加註解,但是注意註解不要影響程式的閱讀
  • 適當的空白,空行。
  • 不要過度使用文字的縮寫,如果不確定縮寫是否其他人看得懂,就寧願用很長的英文單字,也不要用沒有人看得懂的縮寫。
  • 一個 function 的行數不要太多,大約 「 50 行」上限,並且保持 function 簡單容易閱讀。
  • 不要用 小寫 L , 大小寫 O, o 開頭的字母
  • 使用 IDE 摺疊功能,將 function 摺起來縮成一行,如 vim 內建就有 folder 的功能。

寫註解的時候要小心,透過修改變數 or function 命名來達到容易閱讀的方式為最佳,若不是萬不得以,不要寫註解。

程式排版

程式的排版非常的重要,一個好的排版,讀者才能順暢的看懂程式。

條列式

條列式的排版,比較適合大部分人的閱讀方式,舉個例好,下面這個程式就是沒有經過排版,程式碼的變數指定呈現鉅齒狀,不容易閱讀。

Example
  1. $nickname = $person['nickname'];
  2. $age = $erson['age'];
  3. $name = $person['name'];
  4. $address = $person['zipCode'] . $person['address'];

上面那段程式你是否有注意到 第二行的變數 erson 打錯字了,接下來我們來幫程式碼好好的排版。

Example
  1. $nickname = $person['nickname'];
  2. $age = $erson['age'];
  3. $name = $person['name'];
  4. $address = $person['zipCode'] . $person['address'];

經過排版後,第二行的錯字就可以很容易的被發現,因為程式碼的可讀性變高。

程式碼區塊

一個 function 的程式碼行數大約會在 10 ~ 50 行之間,讀懂 50 行程式碼也不算是一個簡單的事,如果我們將程式碼分成多個區塊,再對每一個區塊寫下一行簡短的註解,這樣讀程式碼就更容易了,區塊註解還有另一個功能,一般人沒辦法記住每一個區塊的程式碼邏輯,而註解能提醒讀者每個段落的內容,例如下面這個例子。

Example
  1. function request($url, $params, $header)
  2. {
  3. // Handle http request.
  4. if (preg_match(/\?/, $url)) {
  5. $url .= "&";
  6. } else {
  7. $url .= "?";
  8. }
  9. foreach ($params as $key => $val) {
  10. $url .= $key . "=" . $val;
  11. }
  12.  
  13. // Handle Curl
  14. $tuCurl = curl_init();
  15. curl_setopt($tuCurl, CURLOPT_URL, "https://example.com/xx/");
  16. curl_setopt($tuCurl, CURLOPT_PORT , 443);
  17. $tuData = curl_exec($tuCurl);
  18.  
  19. //Handle response
  20. $response = json_decode($tuData, true);
  21. return $response;
  22. }

tmp 暫存的變數

Example
  1. var tmp = "file_";
  2. for (var i = 0; i , 20; i++) {
  3. tmp += i;
  4. }
  5. var file = tmp + ".txt";
  • tmp_file
  • tmp_username

暫存的變數命名

若是真的有些變數無法命名,那就用一般的變數方式來命名。

  • tmp : 不知道如何命名的暫存變數,都用 tmp
  • retval : return value,用途回傳值
  • it : iterator , 用在 for each 的每一個 item。

明確的變數命名

  • runLocal --> useLocalDatabase
  • disallowMethod --> disallowCopyAndPaste
  • id = "2" , encodeId = "abefcbad****"
  • size -->sizeMB
  • time --> timeMS (Million Second)
  • length --> lenBytes ,
  • mb_strlen('','', 'UTF8') lenUTF8 ,當使用 UTF8 編碼來計算文字長度,一個中文字代表長度 1 ,一個英文字也代表長度 1
  • maxChars : 最大容納的字元
  • maxBytes : 最大 byte 數
  • maxMultipleBytes : 最多中文數
  • MAX_ITEMS_IN_BOX : 這個 box 的最大值

停止事件 , stop , pause, kill

  • stop, start : 停止某一個行為,並可以使用 start 重新啟動一個全新的事件。
  • pause, resume : 暫停某個行為,但是可以重新恢復,繼續先前的進度。
  • kill : 強迫結束,不管是否會造成任何問題。

常用的縮寫單字

除了一些常用的,大家都懂的縮寫單字之外,盡量不要使用其他的縮寫單字,使用過多的縮寫單字,會造成程式閱讀的困難,再加上每個人英文程度不同,很容易造成誤解。

  • str : string
  • len : length
  • eval : evaluation
  • doc : document
  • db : database
  • util : utility
  • ms : million second
  • us : 微秒
  • addr : address
  • 來源 src ,目的地 dest

不好的範例

  • backendManager => BEManager , BEManager 很難看得懂
  • googleTranslator => GT
  • color: clr

不要使用反向意思, un , dis, anti

  • 例如我曾經碰過有人命名 unFold = true ,代表打開這個節點 , unFold = false 代表關閉節點 ,那 if (empty($unFold)) 到底是開還是關呢?
  • use_encode , disable_encode

is, has, can, should , use

  • isAdmin = true ,代表 User 是 Admin
  • hasOrder = false ,代表這個 User 有訂單,不好的寫法是使用 notEmpty 這種反意。
  • useMysql = true

Front-End 相關

  • nodeName: 代表 div, span, body 等等。
  • elm: element 的縮寫 ,代表 html 中某一個 node 的物件。

與他人合作

如果你是單兵作戰部隊,那你想怎麼寫 code 都不會有人理你,但是當你在一個團隊中時,你寫的程式,未來將會有很多人幫你維護,修改,解 Bug ,所以寫 Code 的時候,必須想到接手的人是否能夠快速的理解你寫程式。

變數的名稱要能發音,請使用念得出來的英文字母來當變數,當我們必須使用縮寫或是自創文字,這時盡量組成一個容易發音的單字,這樣與其他工程師討論時會比較容易,或是對新人教學時也會方便很多。

重要變數的數值必須要可以被搜尋到 ,例如我定義訂單的 status = 5 時,代表這是一筆成功的訂單,並且我要更新訂單資訊 , 我可以先定義一個常數變數 STATUS_OPEN = 5 , 這樣當我想查詢 status = 5 ,程式會執行那幾行 Code 時,這時我可以搜尋 STATUS_OPEN 這個字母,而不是搜尋 「5」這個數字 (你如果搜尋 5 的話,會搜尋到相當多的垃圾)。

Example
  1. var STATUS_CLOSE = 4;
  2. var STATUS_OPEN = 5;
  3. if (status == STATUS_SUCCESS) {
  4. updateOrder();
  5. }

其他相關

  • begin , end => first, last , end 代表最後一個,但是沒辦法說明是否包含最後一個,
一行不要太長,可以斷行就斷行
  1. if (aaaaaaaa == "bbbbbbbbb" &&
  2. ccccccc == "adddddddd"
  3. ) {
  4. }
  • 物件分類,類似的功能的 function 放在一起。
Example
  1. class xx
  2. {
  3. //fetch data
  4. function fetchJS(){}
  5. function fetchCSS(){}
  6. //save
  7. function saveJS(){}
  8. function saveCSS(){}
  9. //convert encoding
  10. function convertToUTF8(){}
  11. function convertToBig5(){}
  12. }

Function 的開頭與結尾括號

不管那一種語言, function 都會有一個開頭與結尾大括號 "{}" ,建議括號都能自已獨立一行,例如下面這個範例,好處是,當我們使用 IDE 將 function 做折疊功能時,function 名稱與參數最好不要被折疊,這樣程式碼可讀性比較高。

Example
  1. // 折疊前
  2. void fetchUser(int userId, bool isAdmin)
  3. {
  4. ret = db.query("xxx");
  5.  
  6. }
  7.  
  8. // 好讀的折疊結果
  9. void fetchUser(int userId, bool isAdmin)
  10. +-- 4 lines: {--------------------------------------------------------------------------------------------------------------
  11.  
  12. // 括號不斷行的話,程式碼會折疊成一行
  13. +-- 4 lines: void fetchUser(int userId, bool isAdmin) {------------------

回應 (Leave a comment)