如何写一个大家都看得懂的程式呢? 最近读了 「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) {------------------