上一篇關于函數的文章寫到編寫函數的幾個注意事項:
函數要短小。
函數只應該做一件事。
函數的參數要盡量少,避免使用輸出函數。
函數要無副作用。
函數要區分指令和詢問。
本篇接著寫關于函數的其他知識。
避免傳標識符參數給函數。標識符是指true或false。標識符參數意味著函數至少要做兩件事情:true,做一件事;false,做另一件事。我的代碼、其他同事的代碼,用標識符做參數的函數,很常見。如果要將標識從函數中消除,那么將增加函數個數,而且,在函數中由標識符帶來的if...else...結構將轉移到函數之外。這似乎沒有好處。
指令型函數要使用異常處理代替狀態碼返回。請看代碼A。
if(deleteUserById(id) === ERROR_CODE){
if(deleteUserFriendById(id) === ERROR_CODE){
return true;
}else{
log(errorMsg);
}
}else{
log(errorMsg);
}
deleteUserById(id)、deleteUserFriendById(id)都是指令型函數。將上面的代碼改造為代碼B。
try{
deleteUserById(id);
deleteUserFriendById(id);
}catch(Exception e){
log(e);
}
代碼B明顯簡潔了很多,但是我不明白:不同函數拋出的異常是不同的,捕捉到異常后進行處理的代碼也可能是不同的。假如需要針對不同的代碼,進行不同的處理,必須去了解封裝在函數內部的代碼才行。這違背了“開放—封閉”原則。工作至今,我只用過幾次此類處理錯誤的方式。需要深入函數內部去了解所拋出的異常情況,是我不使用此方式的原因。但這個方式是專家提倡的,我需慢慢琢磨,直至透徹理解它的精妙。
代碼B可以進一步改寫為代碼C。
try{
deleteUserAndFriendById(id);
}catch(Exception e){
log(e);
}
function deleteUserAndFriendById(id){
deleteUserById(id);
deleteUserFriendById(id);
}
從代碼B到代碼C,遵循著編寫函數的又一個原則:把其他大段代碼丑陋不堪的try...catch中剝離出來。至于為什么這么做,除了簡潔,我想不出其他原因。
函數命名,應該使用動詞或動詞短語+名詞的形式。例如,write(name),就是很好的函數名稱。
說點題外話。今日周五,懈怠了,中午一直在看動漫,沒有寫作。
同事前些天推薦使用markdown寫作,今天嘗試了一下。我雖然沒有掌握所有的markdown技巧,但稍加運用,就有不少好處:排版好看了些,尤其是代碼的排版。