Coding Style

代碼是寫給人看的(Coding for Reading)

 

請先思考,評價一段代碼優劣最重要的標準是什麼?

有個著名的圖,相信大家都見過,講的是Code Review時被罵WTF的次數越少,代碼的質量就越高。這雖然有點無厘頭,但是卻不無道理。

《The Art of Readable Code》中告訴我們,評價一段代碼的質量的最佳標準是可讀性,即別人理解代碼意圖所需要的時間。

可讀性有什麼用,我寫出一段代碼,能完成目標,能通過測試不就行了嗎?

不管你是團隊開發,還是一個人單幹,只要項目還在運作,代碼總是要有人來維護的。如果有一天你不在或者離開了,別人應該能很輕鬆的看懂你的代碼,而不是猜來猜去,最後還要來問你,甚至棄用整段代碼重新再寫。作爲一個負責任的開發者,應該尊重自己的勞動成果,也尊重別人的時間。

當你寫下一段代碼時,腦子裏有清晰的邏輯(比如這裏必須這麼做,因爲……這裏不能那麼做,因爲……)。而別人看這段代碼時,腦中是一片空白(這個變量是幹什麼的?爲什麼要多加一層判斷?這個數爲什麼是 2 不是 3 ?),除非能完整復現你當時腦中的邏輯,否則就不能理解這段代碼。

所以我們應該做的是,把寫代碼時腦中想到的邏輯的每一個細節,儘可能地寫到代碼以及註釋中,從而幫助後來的閱讀者快速復原整個邏輯。簡單來說可以有以下的方法:

用常量代替魔術數

在一段代碼中出現 if ( level > 3 ) ...的時候,也許你會想:爲什麼是 3 不是 4 呢?這就是所謂的魔術數。通過上下文也許你可以判斷出這個 3 是指最高等級,但是可能這段代碼中 3 出現了好幾次。你會迷惑:它們是不是都是一個意思?當我要把最高等級改成 4 的時候,是不是應該修改所有的 3 ?

如果代碼的作者定義一個常量MAX_LEVEL = 3,同時還有一個常量USERS_PER_PAGE = 3,並在不同的地方使用不同的常量,就不會有這樣的混淆了。

使用富含信息的類名、變量名和函數名 

doit();
$return = $str2 . ':' . $str3;
return $return;

 這樣的代碼,簡直就是人工混淆過的。你會發現這些代碼根本不能幫助你理解代碼的含義。

也許你該寫成這樣? 

handleError();
$error_message = $error_code . ':' . $error_status;
return $error_message;

在註釋中說明一段代碼存在的原因,而不是行爲

$('#item').html(''); //清空item的內容

 這樣的註釋有意義麼?用自然語言重新描述一次代碼的行爲,除了徒增維護時的工作量外沒有任何價值。你應該說明爲什麼這樣做,以供別人看到這段代碼時明白你是怎麼想的,並決定如何修改或者對待這段代碼。 

$('#item').html(''); //先清空容器的內容,否則可能導致內容重複

 

寫得越少越好(Less is More)

 

這個規則的使用性太強了,我簡單說說減少邏輯層次和縮小函數體這兩個方面吧。

減少邏輯層次

當邏輯層次超過三層時,理解這段代碼的難度會急劇上升。我相信誰也不喜歡去讀一個n層括號的表達式,或者面對n層縮進的條件判斷/循環。

對於複雜的表達式,通過提取中間變量來降低表達式的邏輯層次,保證每個表達式的邏輯層次不超過二層。

對於多層條件判斷,大多數情況可以用防禦式編程將其簡化成單層的條件判斷,儘早return或者exit。此外,單行的if-else判斷往往可以用三元操作符替換。如果判斷實在太多,也許你該重新設計一下結構了。

縮小函數體

記得有一位語言的創始人說過:“我不喜歡比我的頭還大的函數”。

事實上,大家都喜歡短小精幹、一眼就能看到底的代碼。簡潔明快的代碼有助於別人迅速理解代碼的意圖,也方便快速定位問題。如果一個函數要滾動屏幕才能看全,那你往往要不斷地來回滾動,並強迫自己記住一些信息,再返回去看另一部分,這樣做會非常累。

隨着函數體不斷膨脹,理解它所需要的時間隨之增加,出錯的機率也會大大提升。而且越大的函數,可維護性和複用性越差。當部分代碼邏輯需要修改時,不能快速定位到要修改的位置,也難以確定函數體其他位置是否也需要對應的修改。

當函數尺寸失控時,首先要想到的是,有沒有其他方法,用更少的代碼完成這個任務?能不能用正則表達式?能不能用查表法?有沒有內置的庫函數可以利用?

面對一個無法再簡化的流程,將其拆分成細粒度的步驟,將每個步驟的相關代碼分離出來,提取成子函數,再給子函數起一個漂亮的名字。這樣可以降低理解主流程的難度,在做修改時也可以通過函數名快速定位,而且因爲相關的代碼都在一起,不容易漏改。

也許你會懷疑調用函數所造成的性能損失,我想說現在這個時代,手機都馬上四核了……

 

不要重複(Don't Repeat Yourself)

 

看過《重構》一書後,我看到代碼中任何重複的地方都如見眼中釘。

重複是萬惡之源,當你發現你在對代碼的不同部分進行同樣的修改時就要警惕了。改的地方越多,就越可能出錯。也許忘了改一個地方,也許錯改或者刪除了周邊的代碼……永遠不要讓這種事情發生!

將重複的代碼提煉成子函數 

$('#count').text( + $('#count').text() + 1 );
...
$('#count').text( + $('#count').text() + 1 );
...

 如果一段相同的代碼出現兩次,基本上你還會第三次用到它,所以很有必要將其提煉成子函數。這樣不僅可以減少代碼量,還可以降低維護的難度。

複製代碼
countPlusOne();
...
countPlusOne();
...
function countPlusOne() {
    $('#count').text( + $('#count').text() + 1 ); //以後修改只用改這裏就好了
}
複製代碼

如果有幾段代碼很相似,往往可以提取其共性邏輯,使用不同的調用參數進行區分。

複製代碼
countChange( +1 );
...
countChange( -1 );
...
function countChange( change ) {
    $('#count').text( + $('#count').text() + change ); 
}
複製代碼

 用循環減少重複 

$item[ 'id' ] = $_POST[ 'id' ];
$item[ 'name' ] = $_POST[ 'name' ];
$item[ 'mail' ] = $_POST[ 'mail' ];
$item[ 'qq' ] = $_POST[ 'qq' ];

 如果有大塊邏輯雷同只有一兩個地方更改的代碼,往往可以用循環來解決:

foreach ( array( 'id', 'name', 'mail', 'qq' ) as $key ) {
    $item[ $key ] = $_POST[ $key ];
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章