如何寫一個大家都看得懂的程式呢? 最近讀了 「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 命名來達到容易閱讀的方式為最佳,若不是萬不得以,不要寫註解。
程式排版
程式的排版非常的重要,一個好的排版,讀者才能順暢的看懂程式。
條列式
條列式的排版,比較適合大部分人的閱讀方式,舉個例好,下面這個程式就是沒有經過排版,程式碼的變數指定呈現鉅齒狀,不容易閱讀。
- $nickname = $person['nickname'];
- $age = $erson['age'];
- $name = $person['name'];
- $address = $person['zipCode'] . $person['address'];
上面那段程式你是否有注意到 第二行的變數 erson 打錯字了,接下來我們來幫程式碼好好的排版。
- $nickname = $person['nickname'];
- $age = $erson['age'];
- $name = $person['name'];
- $address = $person['zipCode'] . $person['address'];
經過排版後,第二行的錯字就可以很容易的被發現,因為程式碼的可讀性變高。
程式碼區塊
一個 function 的程式碼行數大約會在 10 ~ 50 行之間,讀懂 50 行程式碼也不算是一個簡單的事,如果我們將程式碼分成多個區塊,再對每一個區塊寫下一行簡短的註解,這樣讀程式碼就更容易了,區塊註解還有另一個功能,一般人沒辦法記住每一個區塊的程式碼邏輯,而註解能提醒讀者每個段落的內容,例如下面這個例子。
- function request($url, $params, $header)
- {
- // Handle http request.
- if (preg_match(/\?/, $url)) {
- $url .= "&";
- } else {
- $url .= "?";
- }
- foreach ($params as $key => $val) {
- $url .= $key . "=" . $val;
- }
- // Handle Curl
- $tuCurl = curl_init();
- curl_setopt($tuCurl, CURLOPT_URL, "https://example.com/xx/");
- curl_setopt($tuCurl, CURLOPT_PORT , 443);
- $tuData = curl_exec($tuCurl);
- //Handle response
- $response = json_decode($tuData, true);
- return $response;
- }
tmp 暫存的變數
- var tmp = "file_";
- for (var i = 0; i , 20; i++) {
- tmp += i;
- }
- 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 的話,會搜尋到相當多的垃圾)。
- var STATUS_CLOSE = 4;
- var STATUS_OPEN = 5;
- if (status == STATUS_SUCCESS) {
- updateOrder();
- }
其他相關
- begin , end => first, last , end 代表最後一個,但是沒辦法說明是否包含最後一個,
- if (aaaaaaaa == "bbbbbbbbb" &&
- ccccccc == "adddddddd"
- ) {
- }
- 物件分類,類似的功能的 function 放在一起。
- class xx
- {
- //fetch data
- function fetchJS(){}
- function fetchCSS(){}
- //save
- function saveJS(){}
- function saveCSS(){}
- //convert encoding
- function convertToUTF8(){}
- function convertToBig5(){}
- }
Function 的開頭與結尾括號
不管那一種語言, function 都會有一個開頭與結尾大括號 "{}" ,建議括號都能自已獨立一行,例如下面這個範例,好處是,當我們使用 IDE 將 function 做折疊功能時,function 名稱與參數最好不要被折疊,這樣程式碼可讀性比較高。
- // 折疊前
- void fetchUser(int userId, bool isAdmin)
- {
- ret = db.query("xxx");
- }
- // 好讀的折疊結果
- void fetchUser(int userId, bool isAdmin)
- +-- 4 lines: {--------------------------------------------------------------------------------------------------------------
- // 括號不斷行的話,程式碼會折疊成一行
- +-- 4 lines: void fetchUser(int userId, bool isAdmin) {------------------