此為知乎問答,我把我的答案稍作整理放到這裡來了。
原則
首先說一個最重要的優化原則:程式碼優化是每天都要進行的,而不是一兩個月做一次大優化,那時做就已經晚了。另外由於優化是每天做的,所以你不需要一次的就過度優化,保持小步快跑即可。
這個原則為什麼重要?因為很多程式設計師會在寫程式碼的時候說「先不優化了,等不忙的時候再優化」,然後……就沒有然後了。
基本上「爛程式碼」就是因為「不忙的時候再優化」造成的。
別給自己寫爛程式碼找理由
如果只要每天優化一點點程式碼,就能保持你的程式健康,你,能做到嗎?
據我觀察,90% 的程式設計師做不到。他們每天都會在心裡找出如下理由來寫出爛程式碼,或者對現有的爛程式碼視而不見:
- 這個專案我只維護幾個月,沒必要把程式碼寫那麼好,反正有人接盤。
- 這個專案是從別人手裡接下的,程式碼真爛,要怪就怪之前的人,不是我的錯,我胡亂加一些程式碼就行了,能用就行。
- 這是一個短期專案,沒必要把程式碼寫那麼好
- 這是一個長期專案,明年再優化程式碼,現在能用就行
所以你看,不管我告訴他們多少優化程式碼的技巧,他們根本就不會去用的,這才是問題所在。
很多程式設計師抱怨公司程式碼爛,卻從來不去嘗試解決問題。(就像很多程式設計師抱怨培訓班教出來的人水平差,自己卻不寫新人教程一樣)
過手就變好
如果你不想變成上面那樣的程式設計師,你只堅定一個信念:只要是經過我的手的程式碼,質量就會比原來好一點。
那麼你很快就能把程式碼寫好了。你可能急於聽到把程式碼寫好的技巧,但是我告訴你,技巧真的不重要,這個信念才是最重要的。
接下來就是技巧。
第一步:不要寫爛程式碼
方方你是傻了嗎,問的是「如何優化程式碼」,你的答案居然是「不要寫爛程式碼」?!
沒錯,把程式碼寫好的第一步就是不要寫爛程式碼,也就是你要知道「什麼樣的程式碼是爛程式碼」:
上面這篇教程非常好,把市面上的爛程式碼基本都總結出來了,大概有這麼幾類:
- 爛變數名
- 爛註釋
- 爛設計
- 不寫測試(所有沒有單元測試的程式碼都是爛程式碼,快點學習單元測試!)
基本上所有新人天天都在寫爛變數名、爛註釋和爛設計,而且還不寫單元測試。
而且他們還不知道自己程式碼多爛!
所以第一步就是明白一個真相:你80%的程式碼都是爛程式碼。
你只需要把這些程式碼改得不那麼爛,就是優秀的程式碼了……
再說一次:第一步至關重要,搞清楚什麼樣的程式碼是爛程式碼。
第二步:避免重複
也就是 Don't Repeat Yourself 原則。如果你發現有兩行程式碼重複出現了好幾次,你就應該把這兩行程式碼封裝成一個函式,放在一個恰當的地方,然後呼叫這個函式。
第三步:表驅動程式設計
如果你的程式碼有很多 if ... else ... 結構,你不知道怎麼優化,你就應該使用表驅動程式設計。
優化前:
howManyDays(year, month){
if(month === 1 ||
month === 3 ||
month === 5 ||
month === 7 ||
month === 8 ||
month === 10 ||
month === 12
){
return 31
}else if(month === 2){
return isLeapYear(year) ? 29 : 28
}else{
return 30
}
}
複製程式碼
優化後:
howManyDays(year, month){
const table = {
1: 31, 3: 31, 5: 31, 7: 31, 8: 31, 10: 31, 12:31,
4: 30, 6:30, 9: 30, 11: 30,
2: isLeapYear(year) ? 29 : 28
}
return table[month]
}
複製程式碼
優化前:
function calculateGrade(score){
if(score>=90){
return 'A'
}else if(score >= 80){
return 'B'
}else if(score >= 70){
return 'C'
}else if(score >= 60){
return 'D'
}else {
return 'E'
}
}
複製程式碼
優化後:
function calculateGrade(score){
const table = {
100: 'A',
90: 'A',
80: 'B',
70: 'C',
60: 'D',
others: 'E'
}
return table[Math.floor(score/10)*10] || table['others']
}
複製程式碼
第四步:用套路
設計模式就是一些程式設計套路,Unix 程式設計原則也是一些程式設計套路。
瞭解所有的套路,然後遇到問題選擇正確的套路即可。
比如模組通訊一般用事件模式或者命令模式;
比如解耦一般用中間層;
比如生命週期一般都支援鉤子或切面;
比如效能優化一般都是加快取;
比如 API 設計一定要正交;
比如複雜資料系統一般使用命令查詢職責分離;
比如拿空間換時間拿時間換空間;
……
這一塊還挺複雜的,夠你糾結很久了,而且沒有通解。唯一的通解就是 tradeoff。
第五步:堅持每天優化
我在課上說過,「每天優化」才叫重構,「每年優化」那叫重寫。
優化的重點是「越來越好」,重點不是「一次寫好」。
一旦你放鬆對自己程式碼的要求,你的程式碼就會迅速變成爛程式碼,而且很難恢復。
每當需求變化的時候,你都要重新審視你的整個系統,哪裡有問題你就改那裡,不允許「先臨時改一下以後再優化」,你的程式碼就可以保持健康和活力。
可惜,大部分人做不到。就算我自己也會在需求太多的時候放鬆對程式碼的要求。