代碼就是設計(Jack W.Reeves, 1992)
代碼是最有價值的交付物。我們需要好代碼嗎?在給“好代碼”下個定義之前,這個問題無法回答。那么,究竟什么是好代碼?
看下面這段英文解釋:
'Good code' is code that works, is bug free, and is readable and maintainable. Some organizations have coding 'standards' that all developers are supposed to adhere to, but everyone has different ideas about what's best, or what is too many or too few rules. There are also various theories and metrics, such as McCabe Complexity metrics. It should be kept in mind that excessive use of standards and rules can stifle productivity and creativity. 'Peer reviews', 'buddy checks' code analysis tools, etc. can be used to check for problems and enforce standards.
解釋如下:
好的代碼是代碼運行正常、bug很少、并且具有可讀性和可維護性。一些企業(yè)自己有所有開發(fā)人員都必需遵守的編碼規(guī)范,但是對于什么樣的代碼是最好的每個人的都有自己的標準、或者有太多的或太少的編碼規(guī)則。這有多種原則和標準,例如,McCable 的復雜度度量。的確使用過多的編碼標準和規(guī)則可能降低生產(chǎn)率和創(chuàng)造性。“同行評審”或“同事檢查”代碼分析工具等,都能用來檢查問題或堅持標準。
那么接下來介紹下,什么是好代碼的標準呢,請看下面解釋:
一、代碼命名規(guī)范:
1、 package包名全部由小寫的ASCII字母組成,用“.”分隔。在此項目中,所有的包均以“com.abc.ticket”開頭。
2、 class 類名應當是名詞,每個內(nèi)部單詞的頭一個字母大寫。應當使你的類名簡單和具有說明性。用完整的英語單詞或約定俗成的簡寫命名類名。【示例】public class UserManager
3、 interface接口名應當是名詞,每個內(nèi)部單詞的頭一個字母大寫。應當使你的接口名簡單和具有說明性。用完整的英語單詞或約定俗成的簡寫命名接口名。【示例】interface TicketManagement
4、 Class 成員屬性及變量的命名 () 變量名全部由字母組成,頭一個字母小寫,以后每個內(nèi)部單詞的頭一個字母大寫。變量名應該短而有意義。變量名的選擇應該易于記憶。一個字符的變量名應避免,除非用于臨時變量。通常臨時變量名的命名規(guī)則為:i,j,k,m,n用于整數(shù);c,d,e用于字符。
5、常量的命名,Java 里的常量,是用static final 修飾的,應該用全大寫加下劃線命名,并且盡量指出完整含義。【示例】static final String SMTH_BBS="bbs.tsinghua.edu.cn";
6、數(shù)組的命名,數(shù)組應該總是用下面的形式來命名:byte[] buffer;
7、方法的參數(shù)和變量的命名規(guī)范一致,且應使用有意義的參數(shù)命名,如果可能的話,使用和要賦值的字段一樣的名字。【示例】setCounter(int size){ this.size = size; }
8、 方法 命名()方法的命名應當使用動詞,頭一個字母小寫,以后每個內(nèi)部單詞的頭一個字母大寫。在方法名的選擇上應意義明確便于記憶。對于屬性的存取方法,應使用getXXX()和setXXX()名稱,以isXXX(),hasXXX()來命名返回值為boolean 類型的方法。
以上幾條如果符合就算是好代碼了嗎?當然不是,這只是代碼中最基本的命名規(guī)范而已,就算不符合最多就是代碼不好看,沒什么其他影響。
二、代碼邏輯規(guī)范
1、需求、設計中的重點功能(結合需求/設計的評審產(chǎn)出)
2、代碼格式校驗action/fa?ade等系統(tǒng)入口是否有數(shù)據(jù)格式校驗需要存入數(shù)據(jù)庫的數(shù)據(jù)字段是否有長度校驗
3、分支/循環(huán)if-else/switch是否處理了所有分支分支的條件語句是否有“副作用”;即,條件語句是否會改變系統(tǒng)狀態(tài)/數(shù)據(jù)等循環(huán)邊界是否覆蓋了所有元素是否有死循環(huán)等
4、異常處理是否有“吃掉異常”的情況是否記錄了異常日志如果二次拋出,是否有合理的異常層次/結構如果內(nèi)部處理,對異常的處理是否能保證后續(xù)代碼正常運行
5、單元測試是否有單元測試單元測試是否自動化單元測試是否能完整覆蓋需求
6、 事務處理事務范圍是否合理;或者說,是否把不必要的操作放到了同一個事務中事務傳播方式是否合理(required,never,new等配置)
7、sql語句sql語句是否正確使用mybatis的動態(tài)語句時,是否有潛在的sql語法問題
8、第三方組件使用Redis,RabbitMQ等組件,是否真的對組件完全了解,在使用的過程中是否正確執(zhí)行了開啟與關閉操作。
寫到這里,可能會有不少讀者認為,代碼規(guī)范也就這些了吧,按照上面二類寫完算是優(yōu)秀的代碼了嗎?其實還是遠遠不夠。
三、可讀性,可維護性
曾經(jīng)看過一段代碼,一個method幾千行代碼,所有業(yè)務邏輯都揉在了一起。然后沒有人愿意再維護了,修改一點就會引發(fā)不可預知的錯誤,代碼又臭又長。在這種情況只能重構,于是我在部門內(nèi)部推廣二本書《代碼整潔之道》和《重構-改善既有代碼的設計》并且制訂部門自己的開發(fā)風格,通過組織所有開發(fā)人員練習小項目的開發(fā),使整個部門的開發(fā)風格整齊劃一,不管是老同事還是新同事,都能夠非常快速的上手,程序中依賴度降低,結構非常清晰。
四、性能瓶頸
在真實工作中,很多程序員其實在開發(fā)完程序后不去真正關注程序的性能和響應時間到底如何,憑的是以往開發(fā)經(jīng)驗在開發(fā)的過程中盡可能的去減少問題點。
這樣就只能在生產(chǎn)環(huán)境中去驗證性能問題了,實際這種做法風險較大,所帶來的損失也是較大的,我們在開發(fā)完程序后,不僅要采用Junit或者JMock這樣的工具進行業(yè)務功能自測,更重要是能夠采用相應的工具和命令進行代碼性能和響應時間的測試,在第一關就能夠找出可能出現(xiàn)的一部分問題點,那么經(jīng)常使用的工具和命令如下:top,vmstat,pidstat,Hprof,Btrace,Dtrace等命令。
具體可以參考這兩篇文章:
由多線程內(nèi)存溢出產(chǎn)生的實戰(zhàn)分析
性能分析系列-小命令保證大性能
五、代碼容錯
曾經(jīng)有一個案例:X同事在“統(tǒng)一配置管理系統(tǒng)“中將一個參數(shù)配置在里面,當參數(shù)進行修改的時候相應的程序會馬上做出改變進行相應邏輯調(diào)整,可是另一個A同事在操作這個系統(tǒng)的時候配錯了參數(shù),這時候系統(tǒng)在生產(chǎn)環(huán)境中就開始報錯,以致于應用程序崩潰,邏輯無法進行下去造成較嚴重的生產(chǎn)事故,最后恢復完參數(shù)故障時間已經(jīng)進行了十幾分鐘。針對這個問題當時產(chǎn)生了爭論,到底是配置人員的錯,還是開發(fā)人員的錯。
其實在我看來,到底是誰的問題暫且放在一邊,關鍵是開發(fā)人員是否在寫程序的過程中有沒有多一絲的思考,多考慮一些問題點,程序員要時刻懷著一顆懷疑的心和敬畏的心對待自己寫的程序,像上面的問題我們完全可以做一些異常捕獲和默認設置,在出錯的時候至少能夠讓應用程序跑下去而不能整體報錯,讓用戶無法繼續(xù)使用。
再說一個案例:某部門在開發(fā)“統(tǒng)一配置管理系統(tǒng)”,使用的是Zookeeper組件,而且它的工作原理是當某節(jié)點改變的時候,主動去通知所注冊的系統(tǒng),但是有個前提是如果那些系統(tǒng),有一部分沒有得到通知,有一部分得到了通知該怎么辦,比如某幾個系統(tǒng)在通知的時候正好在重啟,這時候zookeeper就不能通知到相應的系統(tǒng),于是在使用該“統(tǒng)一配置管理系統(tǒng)”的時候,出了生產(chǎn)事故。
其實還應該再重復說一下,程序員應該持有懷疑的精神面對調(diào)用的系統(tǒng)和被調(diào)用的系統(tǒng),不要把穩(wěn)定、安全、可靠寄托于別人身上。
究竟怎樣寫代碼才能算好代碼?這是一個有爭議的話題,每個人的理解可能都不同,關鍵是通過討論這個話題制訂一個符合自己部門要求的規(guī)范,這樣有依據(jù)的代碼才可能成為好的代碼。