一段優秀的代碼,它通常須要知足如下幾個條件:程序員
#統一規範#正則表達式
全部的代碼,第一前提必須是統一規範,而常見的統一規範主要包括有如下內容:數據庫
1)統一編輯器規範編程
在團隊開發中,咱們並不對各個開發人員使用的編輯器作硬性要求,你可使用常見的如Eclipse、WebStrom、Sublime等集成開發環境IDE,也可使用UltraEdit這種,甚至Vim都無所謂。可是咱們仍是會作一些統一規範要求,好比Tab鍵統一使用空格Space替換(一個Tab爲四個Space)、文件編碼統一爲UTF-八、換行的Line Delimiter統一爲Unix的LF而不是Windows的CRLF(PHP代碼尤爲重視這點)等等。設計模式
2)統一代碼風格數組
咱們知道代碼的風格有不少種,尤爲是在涉及到括號的使用上,好比:緩存
K&R風格是最先爲人們所喜好的放個,它是由C語言之父Kernighan和Ritchie在他們的《C程序設計語言》一書中確立的,它也常被認爲是最初和最好的風格,它能夠在一個小屏幕中儘量顯示更多的信息。這個也是我我的最經常使用的風格。安全
int k_and_r() { int a = 0, b = 0; while (a != 10) { a++; b++; } return b; }
懸掛式的風格在空間上顯示上更加開闊,因爲有着更明顯的前括號,也使得代碼更加易於瀏覽;但在豎向空間上佔用更多。app
int exdented() { int a = 0, b = 0; while (a != 10) { a++; b++; } return b; }
縮進風格並不太常見,在這種風格下括號隨代碼一塊兒縮進,這種風格也被稱爲「Whitesmith」風格,由於早期的Whitesmith的C編譯器的示例代碼使用的就是這種風格,我的並不推薦。編程語言
int indented() { int a = 0, b = 0; while (a != 10) { a++; b++; } return b; }
還有一些其餘的括號風格,好比GNU風格是介於懸掛式和縮進式風格之間的一種風格,括號被放置在各個縮進級別的一半的位置
3)統一命名規範
命名包括文件的命名、類的命名、方法的命名、變量的命名等,良好的命名使得代碼易於閱讀,也更加易於維護。命名的方法有不少,常見的的有:
匈牙利命名法是一種有爭議的命名約束,它將關於變量或函數的類型的信息編入它們的名稱當中,要求開頭字母使用變量類型的縮寫,其他部分用變量的英文或中文的縮寫,同時要求單詞的第一個字母大寫。這種命名法最初是在20世紀80年代的Microsoft公司中出現的,並在該公司的Win32 API和MFC庫中獲得了普遍的使用,也所以致使了必定的流行性。之因此被稱爲「匈牙利命名法」,是由於它的創始人Charles Simonyi是匈牙利人。此外,變量名看起來像是使用匈牙利語書寫的,可是要理解它並不容易,不少非Windows的程序員都會被好比lpszFile、rdParam和hwndItem等的奇怪名字給搞糊塗。
int iMyAge; // "i"是int類型的縮寫 char cMyName[0]; // "c"是char類型的縮寫
有的時候又稱爲「小駝峯命名法」,它在Java語言庫以及不少C++代碼庫中獲得了普遍使用,這種命名主要源於其大寫字母的佈局很像駱駝的駝峯,它規定第一個單詞字母小寫,後面其餘的單詞首字母大寫。
int myAge; char myName[0];
這種命名法跟上面的駝峯命名法很相像,惟一區別就是其第一個字母也大寫,因此有時又稱爲「大駝峯式命名法」。
int MyAge; char MyName[0];
這種風格在C++標準庫和GNU Foundation中比較常見,也即用下劃線來隔開不一樣的單詞。
int my_age; int my_name[0];
事實上,採用什麼樣的命名方式都是能夠的(雖然我我的更偏向於駝峯命名法),更重要的是命名必須清晰,好比函數名能夠採用動名詞+靜名詞的組合來命名,而不是用foo和bar這種古怪的名稱。另外,建議在清晰的基礎上保持簡潔,不然也極可能出現相似someTypeWithMeaningfulNaming這種很是冗餘的命名,好比在for循環中使用i而不是使用index就是一種推薦作法。
#簡潔清晰#
什麼叫作簡潔?可以一句代碼解決的事情,就不要寫成兩句代碼。
什麼叫作清晰?雖然一句代碼可以解決事情,但咱們有的時候卻將其拆成了多句代碼,使得其變得可能有點冗餘。
優先程度上,清晰>簡潔。
每一個人寫的代碼,它的後續維護者多是一個初級程序員,若是他不能理解你的代碼邏輯,或者說你的代碼邏輯很難理解,那麼他就有可能會犯一些錯誤。複雜的結構和不經常使用的語言技巧雖然能夠證實你在運算符優先級方面有着熟練經驗,可是這些實際上會扼殺代碼的可維護性。好比下面兩段代碼:
int a = b = c = 10; int result = a * b + b * c - a + b / c; // 下面的代碼雖然不如上面代碼簡潔,可是更加的簡單 int a = 10, b = 10, c = 10; int result = (a * b) + (b * c) - a + (b / c);
清晰的代碼,也每每意味着簡單的代碼。若是對於某段代碼不是基於性能上的須要而寫得複雜,那麼,請保持代碼的簡單清晰。
#必要註釋#
做爲一個負責任的程序員,咱們有義務給咱們的代碼寫註釋,即便在編碼任務再繁重的狀況下。
但咱們也須要注意的是,註釋不是越多越好,咱們更加劇視註釋的質量,而不是數量。不少時候,一個好的命名已經能夠可以幫咱們省下不少註釋。好比下面的代碼:
for (int i = 0; i < wlst.size(); i++) k(wlst[i]); // 上面代碼改爲下面代碼 for (int i = 0; i < widgets.size(); i++) { printWidget(widgets[i]); }
註釋中應該包含的內容包括有:
註釋不該該描述代碼是怎樣運行的,這徹底能夠經過閱讀代碼來了解,你更加應該注意的是描述爲何有些東西要這麼寫,好比下面的代碼的註釋就不是必須的,徹底能夠去掉:
// 循環遍歷全部的widget for (int i = 0; i < widgets.size(); i++) { // 打印這些widget printWidget(widgets[i]); }
不要試着去描述代碼,好比下面的代碼的註釋徹底是無效的:
// index自增 ++index;
不要試圖在註釋中去說明某個代碼的限制條件,更應該地使用代碼自己的機制去實現。還有,當有的時候你發現可能要花大量的註釋來描述某段代碼的功能的時候,更好的作法是用代碼去描述,好比把一大段的代碼拆分紅多個子函數,給每一個子函數賦予更合適的命名等。好比:
// 下面這個方法不容許被類之外訪問 public void getMyAge() { } // 徹底能夠不要上面的註釋,而使用以下的代碼實現 private void getMyAge() { }
註釋不該該給自己的代碼形成分心,好比有些程序員喜歡在if的結束加上// end if (a < 1) 這樣的註釋,而這種註釋則是徹底不必的,只會給本來閱讀代碼形成分心,更合適的作法是經過正確合適的縮進方式等來保證代碼可讀性。
#健壯安全#
咱們在編寫代碼的時候,須要考慮各類方方面面的因素,提升代碼的健壯性。好比,在編寫最多見的登陸代碼時,就須要考慮到用戶輸入的用戶名和密碼的多種狀況,好比:
public void checkLogin(String username, String password) { if (username == null || password == null) { // Error } if (username.trim().equals('') || password.trim().equals('')) { // Error } // do something }
不能只是簡單地檢查用戶名和密碼是否匹配,還必需要考慮到當用戶名或密碼爲空時候的處理邏輯,若是用戶名是手機號,還必須使用正則表達式來檢查所輸入的用戶名是否符合手機號碼樣式等等。
不只於此,對於安全性上的要求,還必需要對用戶輸入的字符進行處理,防止用戶輸入相似<script>alert()</script>的XSS攻擊代碼和相似'or'1'='1的SQL注入攻擊代碼等。
總之,要編寫健壯安全的代碼,必須時刻考慮到:
「防護性編程」中心原則是「不作設想」,也即永遠不要設想用戶會按照咱們寫代碼的預期或要求來使用代碼。一些簡單的防護性規則,好比「檢查全部的輸入」和「驗證全部的運算」,能夠幫咱們把代碼中許多的安全隱患給消除掉。
初級開發人員的代碼在早期必須經由高級開發人員進行代碼審覈,有經驗的高級開發人員可以經過閱讀代碼就能發現不少問題,好比邊界值的判斷上,數組是否會形成溢出和字段長度是否超出數據庫限制等這些都是能夠經過代碼審覈來發現。
一些敏捷開發的TDD測試驅動開發就是這麼作的,它要求代碼必須是能夠被測試的,而且這些能夠被測試的代碼時刻可以經過測試樣例。
#高效性能#
隨着如今處理器的愈來愈強大,不少時候應用的瓶頸並不在代碼而在其餘地方(主要是I/O操做,好比讀寫文件和數據庫等),我的更不推薦爲了所謂的性能而犧牲了代碼的可讀性和可維護性,除非這塊代碼確實是必須優化的。
但咱們也不能仗着處理器的強大就能夠徹底不顧代碼的性能,有些簡單的優化原則在咱們寫代碼的時候就須要時刻遵循。
若是不是立刻就要使用某個文件,那麼就不要打開它;若是暫時不須要某個值,那就不要去計算它;若是沒有某個函數程序也能夠運行,那就不要去調用它。
若是一個可能會致使函數計算無效的條件,那麼對這個條件的判斷最好放在頂部,防止作了過多無用工做,好比:
public int calculate(int number) { if (number == 0) { return 0; } // do something }
因爲循環中是每次都要作的,那麼每次循環中若是某個值都保持不變,則將該值移到循環外面,好比:
for (int i = 0; i < tree.appleCount(); i++) { } // 上面代碼能夠改爲以下 int appleCount = tree.appleCount(); for (int i = 0; i < appleCount; i++) { // do something }
確保將可能失敗的測試放在最前面以節省時間,好比if (condition_one && condition_two),確保condition_one不爲真的可能性比condition_two更大。
好比將公共的代碼提取到共享函數中以免重複計算,或者將某個常常被用到的須要被計算出來的值放到緩存中以備使用。
最後,各類不一樣的編程語言都有其不一樣的優化之處,好比Java中使用加強式foreach循環就比傳統的for循環更加高效,具體到各類語言再具體分析。
#易於擴展#
首先,咱們對於程序的擴展性判斷,並非須要其能支持將來需求的變動的可能性。相反,咱們不提倡對代碼作「過分設計」,代碼的可擴展性更主要的是體如今將來可以對需求變化快速響應上,可以跟隨需求的變化而快速變化,而不是一味不變地支持需求的變化。
可是這也確定也不能成爲咱們偷懶的藉口,咱們仍是可使用一些常規的原則來加強代碼的可擴展性,尤爲是時刻保證代碼的「低耦合」很是重要。
不一樣的功能拆分出來,而不是在一個函數裏完成,好比:
public void init() { // load database // init environment // init ui // do something } // 上面的方法把全部的事情都放在一塊兒,擴展性不好,咱們須要把不一樣的功能拆分紅不一樣的函數 public void loadDatabase() {} public void initEnvironment() {} public void initUi() {} public void init() { loadDatabase(); initEnvironment(); initUi(); }
對於配置文件也是同樣,咱們須要在程序的入口處可以根據當前環境不一樣而加載不一樣的配置文件,好比Web應用中常見的作法是將本地環境、測試環境、正式環境分紅三個配置文件,而後根據當前請求域名的不一樣而加載不一樣的文件,而不是把全部的配置寫到一個文件裏。
對於一些功能相似的,徹底能夠抽象出一個抽象類,在這個抽象類中定義功能接口,而後再在不一樣的子類中實現該接口的不一樣功能,好比Web應用中一種常見的作法是在抽象控制類中定義GET/POST/PUT/DELETE請求協議的接口方法,而後再在不一樣子類中分別實現不一樣的功能細節。
public abstract class BaseController { public void get(); public void post(); public void put(); public void delete(); }
#寫在最後#
上面寫了這麼多,其實說白了提升寫代碼能力惟一的途徑就是多寫代碼,多思考。對於初級程序員來講,把代碼寫得具備可維護性(規範/簡潔/註釋)是最基本的要求,而對於有經驗的程序員來講,還必須考慮代碼的健壯性、安全性、高效性和可擴展性等。寫代碼的時候多考慮下這些東西,常常重構本身的代碼,就離寫出優秀的代碼不遠了。
最後,給你們推薦幾本我的以爲不錯的書籍,這幾本書對個人代碼生涯有着極大的影響。
一、《編程匠藝 編寫卓越的代碼》,電子工業出版社,Pete Goodliffe(著),韓江, 陳玉(譯);
二、《重構 改善既有代碼的設計》,人民郵電出版社,Martin Fowler(著),熊節(譯);
三、《大話設計模式》,清華大學出版社,程傑(著);
四、《Java優化編程(第2版)》,電子工業出版社,林勝利, 王坤茹(編著);