一:介紹
1:為什么需要編碼規(guī)范
編碼規(guī)范對于程序員而言尤為重要,有以下幾個原因:
- 一個軟件的生命周期中,80%的花費在于維護
- 幾乎沒有任何一個軟件,在其整個生命周期中,均由最初的開發(fā)人員來維護
- 編碼規(guī)范可以改善軟件的可讀性,可以讓程序員盡快而徹底地理解新的代碼
- 如果你將源碼作為產(chǎn)品發(fā)布,就需要確任它是否被很好的打包并且清晰無誤,一如你已構(gòu)建的其它任何產(chǎn)品
二:命名
1.包命名
命名規(guī)則:一個唯一包名的前綴總是全部小寫的ASCII 字母并且是一個頂級域名,通常是com,edu,gov,mil,net,org。包名的后續(xù)部分根據(jù)不同機構(gòu)各自內(nèi)部的命名規(guī)范而不盡相同。這類命名規(guī)范可能以特定目錄名的組成來區(qū)分部門 (department) ,項目(project),機器(machine),或注冊名(login names)。
例如: com.huaerlala.cu.activitys
規(guī)約:包命名必須以com.hymobile開始,后面跟有項目名稱(或者縮寫),再后面為模塊名或?qū)蛹壝Q。
如:com.huaerlala.項目縮寫.模塊名 --> com.huaerlala.nloc.bookmark
如:com.huaerlala.項目縮寫.層級名 --> com.huaerlala.nloc.activities
2.類和接口命名
命名規(guī)則:類名是個一名詞,采用大小寫混合的方式,每個單詞的首字母大寫。盡量使你的類名簡潔而富于描述。使用完整單詞,避免縮寫詞(除非該縮寫詞被更廣泛使用,像 URL,HTML)
接口一般要使用able、ible、er 等后綴
例如: class Raster; class ImageSprite; ImagePagerChangeListener
類命名規(guī)范:
Activity --> LoginActivity
Fragment --> HomeFm
Model --> BaseModel
Parser --> BaseParser
Util --> DataUtils
View --> HWebView
…
其余略
3:方法的命名
命名規(guī)則:方法名是一個動詞,采用大小寫混合的方式,第一個單詞的首字母小寫,其后單詞的首字母大寫。
例如: public void run(); public String getBookName();
類中常用方法的命名:
類的獲取方法(一般具有返回值)一般要求在被訪問的字段名前加上get,
如getFirstName(),getLastName()。
一般來說,get前綴方法返回的是單個值,find前綴的方法返回的是列表值。
類的設(shè)置方法(一般返回類型為void):被訪問字段名的前面加上前綴 set,
如setFirstName(),setLastName().
類的布爾型的判斷方法一般要求方法名使用單詞 is或has 做前綴,
如isPersistent(),isString()。
或者使用具有邏輯意義的單詞,例如equal 或equals。
類的普通方法一般采用完整的英文描述說明成員方法功能,第一個單詞盡可能采用動詞,首字母小寫,
如openFile(),addCount()。
4:變量命名
命名規(guī)則:第一個單詞的首字母小寫,其后單詞的首字母大寫。變量名不應(yīng)以下劃線或美元符號開頭,盡管這在語法上是允許的。變量名應(yīng)簡短且富于描述。變量名的選用應(yīng)該易于記憶,即,能夠指出其用途。盡量避免單個字符的變量名,除非是一次性的臨時變量。臨時變量通常被取名為 i,j,k,m 和 n,它們一般用于整型;c,d,e,它們一般用于字符型。
例如:String bookName;
規(guī)約:變量命名也必須使用駝峰規(guī)則,但是首字母必須小寫,變量名盡可能的使用名詞或名詞詞組。同樣要求簡單易懂,不允許出現(xiàn)無意義的單詞。
如:String bookName; 正確!
如:String bookNameString; 錯誤!
5:成員變量命名
同變量命名,但不要在私有變量前添加m字樣!
6:常量命名
命名規(guī)則:類常量的聲明,應(yīng)該全部大寫,單詞間用下劃線隔開。
例如:static final int MIN_WIDTH = 4;
例如:static final int MAX_WIDTH = 999;
例如:static final int GET_THE_CPU = 1;
7:異常命名
自定義異常的命名必須以Exception為結(jié)尾。已明確標示為一個異常。
8:layout 命名
規(guī)約:layout xml 的命名必須以 全部單詞小寫,單詞間以下劃線分割,并且使用名詞或名詞詞組,即使用 模塊名_功能名稱 來命名。
如:knowledge_gained_main.xml正確
如:list_book.xml錯誤!
規(guī)約:ListView 或者RecyclerView的item:
模塊名_list_item 來命名。
如:mine_list_item.xml
9:id 命名
規(guī)約:layout 中所使用的id必須以全部單詞小寫,單詞間以下劃線分割,并且使用名詞或名詞詞組,并且要求能夠通過id直接理解當前組件要實現(xiàn)的功能。
如:某TextView @+id/textbookname 錯誤 !應(yīng)為 @+id/book_name_show_tv
如:某EditText @+id/textbookname 錯誤 !應(yīng)為 @+id/book_name_et
Button --> bt
CheckBox --> cb
RadioButton --> rb
RadioGroup --> rg
ImageView --> iv
ImageButton --> ib
…其余略
10:資源命名
規(guī)約:layout中所使用的所有資源(如drawable,style等)命名必須以全部單詞小寫,單詞間以下劃線分割,并且盡可能的使用名詞或名詞組,即使用 模塊名_用途 來命名。如果為公共資源,如分割線等,則直接用用#####11:途來命名
如:menu_icon_navigate.png ? 正確
如:某分割線:line.png 或 separator.png ?正確
三:注釋
Java 程序有兩類注釋:實現(xiàn)注釋(implementation comments)和文檔注釋(document comments)。實現(xiàn)注釋是使用/.../和//界定的注釋。文檔注釋(被稱為"doc comments")由/*.../界定。文檔注釋可以通過javadoc 工具轉(zhuǎn)換成HTML 文件。
1:文件注釋
所有的源文件都應(yīng)該在開頭有一個注釋,其中列出類名、版本信息、日期和版權(quán)聲明。
如下:
/*
*文件名
*包含類名列表
*版本信息,版本號
*創(chuàng)建日期。
*版權(quán)聲明
*/
2:類注釋
每一個類都要包含如下格式的注釋,以說明當前類的功能等。
/**
*類名
*@author 作者 <br/>
*實現(xiàn)的主要功能。
*創(chuàng)建日期
*修改者,修改日期,修改內(nèi)容。
*/
3:方法注釋
每一個方法都要包含 如下格式的注釋 包括當前方法的用途,當前方法參數(shù)的含義,當前方法返回值的內(nèi)容和拋出異常的列表。
/**
*
* 方法的一句話概述
* <p>方法詳述(簡單方法可不必詳述)</p>
* @param s 說明參數(shù)含義
* @return 說明返回值含義
* @throws IOException 說明發(fā)生此異常的條件
* @throws NullPointerException 說明發(fā)生此異常的條件
*/
4:類成員變量和常量注釋
成員變量和常量需要使用java doc形式的注釋,以說明當前變量或常量的含義
/**
*XXXX含義
*/
5:其他注釋
方法內(nèi)部的注釋 如果需要多行 使用/…… /形式,如果為單行是用//……形式的注釋。不要再方法內(nèi)部使用 java doc 形式的注釋“/……**/”,簡單的區(qū)分方法是,java doc形式的注釋在 eclipse中為藍色,普通注釋為綠色。
6:XML注釋
規(guī)約:如果當前l(fā)ayout 或資源需要被多處調(diào)用,或為公共使用的layout(若list_item),則需要在xml寫明注釋。要求注釋清晰易懂。
四:代碼風(fēng)格
1:縮進
規(guī)約:使用Tab進行縮進
2:空行
空行將邏輯相關(guān)的代碼段分隔開,以提高可讀性。
下列情況應(yīng)該總是使用空行:
一個源文件的兩個片段(section)之間
類聲明和接口聲明之間
兩個方法之間
方法內(nèi)的局部變量和方法的第一條語句之間
一個方法內(nèi)的兩個邏輯段之間,用以提高可讀性
規(guī)約:通常在 變量聲明區(qū)域之后要用空行分隔,常量聲明區(qū)域之后要有空行 分隔,方法聲明之前要有空行分隔。
3:行寬
無特別規(guī)定,因為現(xiàn)在的顯示器都比較大,所以推薦使用120進行設(shè)置。
五:規(guī)約
1:方法
一個方法盡量不要超過15行,如果方法太長,說明當前方法業(yè)務(wù)邏輯已經(jīng)非常復(fù)雜,那么就需要進行方法拆分,保證每個方法只作一件事。
不要使用 try catch 處理業(yè)務(wù)邏輯?。。?!
2:參數(shù)和返回值
一個方法的參數(shù)盡可能的不要超過4個!
如果一個方法返回的是一個錯誤碼,請使用異常?。?br>
盡可能不要使用null, 替代為異常 或者使用空變量 如返回 List 則可以使用Collections.emptyList()
3:神秘的數(shù)
代碼中不允許出現(xiàn)單獨的數(shù)字,字符!如果需要使用數(shù)字或字符,則將它們按照含義封裝為靜態(tài)常量!(for語句中除外)
4:控制語句
判斷中如有常量,則應(yīng)將常量置于判斷式的右側(cè)。如:
if ( true == isAdmin())...
盡量不使用三目條件的嵌套。
所有if 語句必須用{}包括起來,即便是只有一句:
if (true){
//do something......
}
if (true)
i = 0; //不要使用這種
對于循環(huán):
//不推薦方式____________________________________________
while(index < products.getCount()){
//每此都會執(zhí)行一次getCount()方法,
//若此方法耗時則會影響執(zhí)行效率
//而且可能帶來同步問題,若有同步需求,請使用同步塊或同步方法
}
//推薦方式______________________________________________
//將操作結(jié)構(gòu)保存在臨時變量里,減少方法調(diào)用次數(shù)
final int count = products.getCount();
while(index < count){
}
5:異常的捕捉處理
通常的思想是只對錯誤采用異常處理:邏輯和編程錯誤,設(shè)置錯誤,被破壞的數(shù)據(jù),資源耗盡,等等。
通常的法則是系統(tǒng)在正常狀態(tài)下以及無重載和硬件失效狀態(tài)下,不應(yīng)產(chǎn)生任何異常。
最小化從一個給定的抽象類中導(dǎo)出的異常的個數(shù)。對于經(jīng)常發(fā)生的可預(yù)計事件不要采用異常。不要使用異常實現(xiàn)控制結(jié)構(gòu)。
若有finally 子句,則不要在try 塊中直接返回,亦不要在finally 中直接返回。
6:訪問控制
若沒有足夠理由,不要把實例或類變量聲明為公有。通常,實例變量無需顯式的設(shè)置(set)和獲取(gotten),通常這作為方法調(diào)用的邊緣效應(yīng) (side effect)而產(chǎn)生。
一個具有公有實例變量的恰當例子,是類僅作為數(shù)據(jù)結(jié)構(gòu),沒有行為。亦即,若你要使用一個結(jié)構(gòu)(struct)而非一個類(如果java 支持結(jié)構(gòu)的話),那么把類的實例變量聲明為公有是合適的。
六:約定俗成
1:變量賦值
避免在一個語句中給多個變量賦相同的值。它很難讀懂。例如:
fooBar.fChar = barFoo.lchar = 'c';
不要將賦值運算符用在容易與相等關(guān)系運算符混淆的地方。例如:
if (c++ = d++) { // AVOID! (Java disallows)
...
}
應(yīng)該寫成
if ((c++ = d++) != 0) {
...
}
不要使用內(nèi)嵌(embedded)賦值運算符試圖提高運行時的效率,這是編譯器的工作。例如:
d = (a = b + c) + r; // AVOID!
應(yīng)該寫成
a = b + c;
d = a + r;
2:圓括號
一般而言,在含有多種運算符的表達式中使用圓括號來避免運算符優(yōu)先級問題,是個好方法。
即使運算符的優(yōu)先級對你而言可能很清楚,但對其他人未必如此。你不能假設(shè)別的程序員和你一樣清楚運算符的優(yōu)先級。
if (a == b && c == d) // AVOID! 注:這里采用這個
if ((a == b) && (c == d)) // RIGHT
3:返回值
設(shè)法讓你的程序結(jié)構(gòu)符合目的。例如:
if (booleanExpression) {
return true;
} else {
return false;
}
應(yīng)該代之以如下方法:
return booleanExpression
類似地:
if (condition) {
return x;
}
return y;
應(yīng)該寫做:
return (condition ? x : y);
條件運算符"?"前的表達式
如果一個包含二元運算符的表達式出現(xiàn)在三元運算符" ? : "的"?"之前,那么應(yīng)該給表達式添上一對圓括號。例如:
(x >= 0) ? x : -x
七:種代碼的壞味道
應(yīng)該在編程中盡量避免這21種“壞味道”。
1:Duplicated Code
代碼重復(fù)幾乎是最常見的異味了。他也是Refactoring 的主要目標之一。代碼重復(fù)往
往來自于copy-and-paste 的編程風(fēng)格。
2:Long method
它是傳統(tǒng)結(jié)構(gòu)化的“遺毒“。一個方法應(yīng)當具有自我獨立的意圖,不要把幾個意圖
放在一起。
3:Large Class
大類就是你把太多的責(zé)任交給了一個類。這里的規(guī)則是One Class One Responsibility。
4:Divergent Change
一個類里面的內(nèi)容變化率不同。某些狀態(tài)一個小時變一次,某些則幾個月一年才變一次;某些狀態(tài)因為這方面的原因發(fā)生變化,而另一些則因為其他方面的原因變一次。面向?qū)ο蟮某橄缶褪前严鄬Σ蛔兊暮拖鄬ψ兓喔綦x。把問題變化的一方面和另一方面相隔離。這使得這些相對不變的可以重用。問題變化的每個方面都可以單獨重用。這種相異變化的共存使得重用非常困難。
5:Shotgun Surgery
這正好和上面相反。對系統(tǒng)一個地方的改變涉及到其他許多地方的相關(guān)改變。這些變化率和變化內(nèi)容相似的狀態(tài)和行為通常應(yīng)當放在同一個類中。
6:Feature Envy
對象的目的就是封裝狀態(tài)以及與這些狀態(tài)緊密相關(guān)的行為。如果一個類的方法頻繁用get 方法存取其他類的狀態(tài)進行計算,那么你要考慮把行為移到涉及狀態(tài)數(shù)目最多的那個類。
7:Data Clumps
某些數(shù)據(jù)通常像孩子一樣成群玩耍:一起出現(xiàn)在很多類的成員變量中,一起出現(xiàn)在許多方法的參數(shù)中…..,這些數(shù)據(jù)或許應(yīng)該自己獨立形成對象。
8:Primitive Obsession
面向?qū)ο蟮男率滞ǔA?xí)慣使用幾個原始類型的數(shù)據(jù)來表示一個概念。譬如對于范圍,他們會使用兩個數(shù)字。對于Money,他們會用一個浮點數(shù)來表示。因為你沒有使用對象來表達問題中存在的概念,這使得代碼變的難以理解,解決問題的難度大大增加。好的習(xí)慣是擴充語言所能提供原始類型,用小對象來表示范圍、金額、轉(zhuǎn)化率、郵政編碼等等。
9:Switch Statement
基于常量的開關(guān)語句是OO 的大敵,你應(yīng)當把他變?yōu)樽宇?、state 或strategy。
10:Parallel Inheritance Hierarchies
并行的繼承層次是shotgun surgery 的特殊情況。因為當你改變一個層次中的某一個類時,你必須同時改變另外一個層次的并行子類。
11:Lazy Class
一個干活不多的類。類的維護需要額外的開銷,如果一個類承擔(dān)了太少的責(zé)任,應(yīng)當消除它。
12:Speculative Generality
一個類實現(xiàn)了從未用到的功能和通用性。通常這樣的類或方法唯一的用戶是testcase。不要猶豫,刪除它。
13:Temporary Field
一個對象的屬性可能只在某些情況下才有意義。這樣的代碼將難以理解。專門建立一個對象來持有這樣的孤兒屬性,把只和他相關(guān)的行為移到該類。最常見的是一個特定的算法需要某些只有該算法才有用的變量。
14:Message Chain
消息鏈發(fā)生于當一個客戶向一個對象要求另一個對象,然后客戶又向這另一對象要求另一個對象,再向這另一個對象要求另一個對象,如此如此。這時,你需要隱藏分派。
15:Middle Man
對象的基本特性之一就是封裝,而你經(jīng)常會通過分派去實現(xiàn)封裝。但是這一步不能走得太遠,如果你發(fā)現(xiàn)一個類接口的一大半方法都在做分派,你可能需要移去這個中間人。
16:Inappropriate Intimacy
某些類相互之間太親密,它們花費了太多的時間去磚研別人的私有部分。對人類而言,我們也許不應(yīng)該太假正經(jīng),但我們應(yīng)當讓自己的類嚴格遵守禁欲主義。
17:Alternative Classes with Different Interfaces
做相同事情的方法有不同的函數(shù)signature,一致把它們往類層次上移,直至協(xié)議一致。
18:Incomplete Library Class
要建立一個好的類庫非常困難。我們大量的程序工作都基于類庫實現(xiàn)。然而,如此廣泛而又相異的目標對庫構(gòu)建者提出了苛刻的要求。庫構(gòu)建者也不是萬能的。有時候我們會發(fā)現(xiàn)庫類無法實現(xiàn)我們需要的功能。而直接對庫類的修改有非常困難。這時候就需要用各種手段進行Refactoring。
19:Data Class
對象包括狀態(tài)和行為。如果一個類只有狀態(tài)沒有行為,那么肯定有什么地方出問題了。
20:Refused Bequest
超類傳下來很多行為和狀態(tài),而子類只是用了其中的很小一部分。這通常意味著你的類層次有問題。
21:Comments
經(jīng)常覺得要寫很多注釋表示你的代碼難以理解。如果這種感覺太多,表示你需要Refactoring。