隨著《阿里巴巴Java開發(fā)手冊(cè)》的公開,重新又掀起一股編碼規(guī)范的風(fēng)口奢米。結(jié)合《華為java編程規(guī)范》以及團(tuán)隊(duì)內(nèi)部的實(shí)踐,我們也做了一段開發(fā)規(guī)范鬓长。不求最全,但求有效涉波。
里面的規(guī)范,暫時(shí)只分兩類苍日。“強(qiáng)制”相恃,即如果違反就不能使用級(jí)別笨觅。比如說(shuō),在codereview有遇到 屋摇,那就會(huì)直接把pull request打回去,拒絕合并到開發(fā)者穩(wěn)定分支上火脉。“推薦”倦挂,即建議怎么做担巩,但是不強(qiáng)制,根據(jù)不同的水平可以做一些參考涛癌。
通用規(guī)范
所有的情況下都通用
1送火、 【強(qiáng)制】命名全部使用英文种吸,禁止中文或者中英混合。項(xiàng)目名除外坚俗,因?yàn)橛械捻?xiàng)目是按域名來(lái)命名的岸裙,域名本身有可能是中文拼音。
例子:
域名:kecheng.xxx.com
項(xiàng)目名:xxx-web-kecheng
2降允、 【強(qiáng)制】禁止使用縮寫,除非提供一個(gè)縮寫列表
反例:
# 這里的t到底是什么意思判呕?topic_id?還是teacher_id?
字段:t_id
3送滞、 【強(qiáng)制】禁止出現(xiàn)除了后綴或者前綴3個(gè)單詞。如果超過(guò)3個(gè)犁嗅,說(shuō)明想表達(dá)的職責(zé)太多,可以拆分或者封裝褂微。
編程語(yǔ)言
這里主要指的是Java語(yǔ)言,其他的語(yǔ)言也可以借鑒這些準(zhǔn)則
類
1式撼、 【強(qiáng)制】需要有統(tǒng)一的后綴或者前綴。為了一看類名著隆,就知道這個(gè)類干什么的呀癣。
前綴列表:
- 抽象類(Abstract)
- 接口(I)
正例:
接口:IViewTag
抽象類:AbstractViewTag
具體實(shí)現(xiàn)類:UserViewTag
后綴列表:
- 實(shí)體(Entity)。數(shù)據(jù)庫(kù)持久對(duì)象浦辨。
- 表單(Form)。用于封裝流酬、校驗(yàn)http參數(shù)。
- 數(shù)據(jù)傳輸對(duì)象(DTO)芽腾。用于暴露接口的返回?cái)?shù)據(jù)
- 基礎(chǔ)服務(wù)(BaseService)。單實(shí)體可以自描述的服務(wù)晦嵌。
- 業(yè)務(wù)服務(wù)(BusinessService)惭载。集合多個(gè)單實(shí)體的服務(wù)响巢。
- 頁(yè)面服務(wù)(ViewService)。涉及到視圖頁(yè)面的服務(wù)踪古。
- 模塊(Module)。http入口模塊伏穆。
- 異常(Exception)
- 工具(Util)
- 枚舉(Enum)
- 視圖標(biāo)簽(ViewTag)
- ....(其他的,比如:Filter之類)
正例:
實(shí)體:UserEntity
基礎(chǔ)服務(wù):UserBaseService
業(yè)務(wù)服務(wù):AuthorityBusinessService
2陪腌、 【強(qiáng)制】所有參與業(yè)務(wù)的類禁止使用內(nèi)部類烟瞧。
屬性
1、 【強(qiáng)制】常量必須是:大寫+下劃線参滴,禁止多個(gè)單詞連在一起
正例:
private final static String PAGE_SIZE=10;
反例:
private final static String PAGESIZE=10;
private final static String pageSize=10;
2、 【強(qiáng)制】布爾類型禁止添加"is"前綴蝌箍。部分框架解析會(huì)引起序列化錯(cuò)誤暴心。
反例:
# 對(duì)應(yīng)的getter和setter為:isRead和setRead
private boolean isRead
正例:
# 對(duì)應(yīng)的getter和setter為:isRead和setRead
private boolean read;
3、 【強(qiáng)制】計(jì)數(shù)器禁止使用復(fù)數(shù)
反例:
private int readCounts;
正例:
private int readCount;
4酷勺、 【強(qiáng)制】自描述屬性里不要出現(xiàn)類名的描述
反例:
#UserEntity類
private String userName;
private int userAge;
正例:
#UserEntity類
private String name;
private int age;
5、【強(qiáng)制】關(guān)聯(lián)其他實(shí)體的屬性命名規(guī)則:對(duì)應(yīng)的實(shí)體去掉后綴+用途
正例:
屬性名:teacherId ,對(duì)應(yīng)的實(shí)體是TeacherEntity
屬性名:favorCount甚亭,對(duì)應(yīng)的實(shí)體是FavorEntity
反例:
屬性名:tId。根本不知道是哪個(gè)實(shí)體的外鍵亏狰。有可能是Teacher有可能是Topic,還得猜半天
6暇唾、 【強(qiáng)制】禁止通過(guò)定義定義成常量(1,2)來(lái)維護(hù)類型值瘸味,需要通過(guò)枚舉
反例:
private final static int SUCESS=1;
private final static int FAIL=2;
正例:
定義一個(gè)枚舉
方法
1够挂、 【強(qiáng)制】接口里的方法禁止有修飾符。
反例
#接口里的方法public void eat();
正例:
#接口里的方法void eat();
2孽糖、 【推薦】方法參數(shù)必須使用final來(lái)修飾。final可提高程序響應(yīng)效率尘奏〔◎龋可以通過(guò)Eclipse的cleanup來(lái)實(shí)現(xiàn)。
正例:
public void eat(final int size);
反例:
public void eat(int size);
3铡恕、 【強(qiáng)制】每一個(gè)方法參數(shù)都需要被處理。module層的方法里的對(duì)象參數(shù)
可以不判空驹针,因?yàn)榧軜?gòu)已經(jīng)做處理了,不可能為空柬甥。
被處理指的是:
- 拋異常其垄。
- 直接返回。
- 有對(duì)應(yīng)的業(yè)務(wù)處理邏輯绿满。
例子:
public void add(long userId,String content){
//異常驗(yàn)證
ExceptionUtil.checkId(userId,"用戶id")
//直接返回
if(Util.isEmpty(content)){
return ;
}
}
public List<CourseEntity> list(int type){
Cnd cnd = Cnd.limit();
//有對(duì)應(yīng)的業(yè)務(wù)邏輯處理
if(type>0){
cnd.and("type","=",type);
}
return dbDao.query(CourseEntity.class,cnd,null);
}
4、 【強(qiáng)制】同一個(gè)類里有多個(gè)一致的參數(shù)(3個(gè)以上)的方法漏健,需要抽取接口或者通過(guò)實(shí)體來(lái)承載
反例
public FavorEntity add(int type,long sourceId,long userId);
public FavorEntity delete(int type,long sourceId,long userId);
正例
public FavorEntity add(IFavor favor);
public FavorEntity delete(IFavor favor);
5、 【強(qiáng)制】方法名必須是動(dòng)詞或者動(dòng)賓殖属。http接口需要知明達(dá)意,可以不按這個(gè)規(guī)則洗显。比如:mycourse,home,banner
方法命名格式:
- is+動(dòng)詞|形容詞
- 動(dòng)詞【+名詞|形容詞】
例子:
public void isSucess();
public void on();
public void sendEmail();
統(tǒng)一命名列表:
- add 新增
- update 修改
- delete 刪除
- get 獲取單個(gè)對(duì)象
- list 獲取集合對(duì)象
- getMap 獲取map數(shù)據(jù)
- count 數(shù)量
方法前綴后綴命名說(shuō)明:
原則上不添加后綴原环,如果添加后綴的話,如果有添加,命名格式為:updatexxxx4yyyyByzzzz
- xxxx:表示對(duì)象的屬性
- yyyy:表示查詢的條件(根據(jù)自描述屬性查詢)
- zzzz:表示查詢的條件(根據(jù)其他描述屬性查詢)
例子:
--xxxx情況:用戶
public void updateName();();
public void updateNickName();
--yyyy情況:資訊
public List<NewsEntity> list4Latest();
public List<NewsEntity> list4Top();
--zzzz情況:課程
public List<CourseEntity> listByTeacher();
public List<CourseEntity> listByKnowledge();
--綜合使用
public List<CourseEntity> listCourse4TopByTeacher();
6碧库、 【強(qiáng)制】一個(gè)方法里代碼行數(shù)不能超過(guò)1屏(即30行)。一般來(lái)說(shuō)超過(guò)30的行嵌灰,業(yè)務(wù)關(guān)注點(diǎn)、復(fù)雜數(shù)比較高迁匠,很難維護(hù)。超過(guò)30行需要封裝方法
7城丧、 【強(qiáng)制】局部變量命名不能有連續(xù)的名稱豌鹤。連續(xù)的命名不具有可維護(hù)性。每個(gè)變量都需要有清晰的概念布疙。
反例:
String head1;
String head2;
正例:
String title;
String content;
8、 【強(qiáng)制】禁止有任何魔鬼數(shù)據(jù)獨(dú)立存在灵临。可以定義一個(gè)有含義的變量來(lái)承載
反例:
if(type ==1){
//審核成功
下面15行代碼
}
正例:
private final static int SUCESS=1;
...
.if(type ==SUCESS){
下面15行代碼
}
9宦焦、 【強(qiáng)制】判斷表達(dá)式要使用布爾變量或者封裝方法。表達(dá)式是變化點(diǎn)赶诊。在維護(hù)的時(shí)候,表達(dá)式不知名達(dá)意舔痪。
反例:
if(user!=null&&!Util.isEmpty(user.name)&&!Util.isEmpty(user.provicne)){
//下面15行代碼
}
正例:
if(isFilledBaseInfo(user)){
//下面15行代碼
}
10、【強(qiáng)制】if()...else if()...else個(gè)數(shù)不能多于4個(gè)夺英,嵌套不能深于3層
可以通過(guò)以下的方法來(lái)消除:
- 設(shè)計(jì)模式
- 抽取方法
- 使用return
反例:
if(isAdmin()){
...
}else if(isTeacher()){
...
}
正例:
if(isAdmin()){
... return;
}if(isTeacher()){
... return;
}
11滋捶、 【推薦】采用防御式編程,先判斷錯(cuò)誤的業(yè)務(wù)重窟,然后再寫正確的業(yè)務(wù)。防御式編程結(jié)構(gòu)清晰分明:先把所有錯(cuò)誤窮舉巡扇,然后集中處理正確邏輯。
反例:
if(null!=user && user.hasAuth()){
正確邏輯
}
正例:
if(null==user || 乖坠!user.hasAuth()){ return;}
正確邏輯
12刀闷、 【推薦】for里不建議寫io。io包括:數(shù)據(jù)庫(kù)甸昏、緩存,文件讀寫等
13施蜜、 【強(qiáng)制】多個(gè)不同的結(jié)構(gòu)(業(yè)務(wù)相近的代碼),需要有且只有一個(gè)空行
反例:
long userId = fetchUser.getCurrentUserId();
Sql sql = latentCustomerQueryForm.pager(sqlManager);
Map<String, Object> map = FormUtil.list(dbDao, sql, pager);
List<DictInfoEntity> grades = dictBaseService.listDict(DictInfoEnum.GRADE.stringKey());
List<DictInfoEntity> infoOrigins = dictBaseService.listDict(DictInfoEnum.INFOORIGIN.stringKey());
map.put("queryForm", latentCustomerQueryForm);
map.put("grades", grades);
map.put("infoOrigins", infoOrigins);
return map;
正例:
long userId = fetchUser.getCurrentUserId();
Sql sql = latentCustomerQueryForm.pager(sqlManager);
Map<String, Object> map = FormUtil.list(dbDao, sql, pager);
List<DictInfoEntity> grades = dictBaseService.listDict(DictInfoEnum.GRADE.stringKey());
List<DictInfoEntity> infoOrigins = dictBaseService.listDict
map.put("queryForm", latentCustomerQueryForm);
map.put("grades", grades);
map.put("infoOrigins", infoOrigins);
return map;
14悬秉、 【推薦】不參與計(jì)算的變量不要定義變量
反例:
long userId = fetchUser.getCurrentUserId();
Sql sql = latentCustomerQueryForm.pager(sqlManager);
Map<String, Object> map = FormUtil.list(dbDao, sql, pager);
List<DictInfoEntity> grades = dictBaseService.listDict(DictInfoEnum.GRADE.stringKey());
List<DictInfoEntity> infoOrigins = dictBaseService.listDict(DictInfoEnum.INFOORIGIN.stringKey());
map.put("queryForm", latentCustomerQueryForm);
map.put("grades", grades);
map.put("infoOrigins", infoOrigins);
return map;
正例:
long userId = fetchUser.getCurrentUserId();
Sql sql = latentCustomerQueryForm.pager(sqlManager);
Map<String, Object> map = FormUtil.list(dbDao, sql, pager);
map.put("grades", dictBaseService.listDict(DictInfoEnum.GRADE.stringKey()));
map.put("infoOrigins", dictBaseService.listDict(DictInfoEnum.INFOORIGIN.stringKey()));
return map;
15和泌、 【強(qiáng)制】如果有捕獲異常,必須有對(duì)應(yīng)的處理業(yè)務(wù)武氓。如果沒(méi)有對(duì)應(yīng)的處理業(yè)務(wù),不要捕獲县恕,可以直接throw,讓架構(gòu)統(tǒng)一處理
你自己catch忠烛,肯定不希望用戶看到錯(cuò)誤日志,那么 從用戶那邊看到是正常業(yè)務(wù)冤议。catch了什么都沒(méi)干师坎,用戶往往看到的是什么都沒(méi)發(fā)生,他會(huì)以為網(wǎng)站掛了或者功能快胯陋。
16、 【強(qiáng)制】禁止使用exception.getMessge()處理錯(cuò)誤信息遏乔。應(yīng)該使用exception.toString()。因?yàn)閑xception.getMessage()按灶,在npe拋出異常的時(shí)候筐咧,什么信息都不顯示。
反例:
} catch (Exception e) {
logger.error(e.getMessage());
}
正例:
} catch (Exception e) {
logger.error(e.toString());
}
17量蕊、 【強(qiáng)制】禁止使用System.out.print。統(tǒng)一使用Eclipse的log4e插件生成日志(不要定義具體的日志實(shí)現(xiàn)残炮,要定義的是slf4j的接口)
18、 【推薦】公開的接口泉瞻,一旦發(fā)布成穩(wěn)定版苞冯,禁止修改方法簽名(方法名,參數(shù))
如果要修改,需要提供新的接口舅锄,老的不能修改。因?yàn)橐恍薷姆椒ê灻?br>
比如:js調(diào)用可能就報(bào)錯(cuò)了,功能就沒(méi)辦法使用坦仍;工具類接口一變叨襟,其他項(xiàng)目就會(huì)報(bào)錯(cuò)了,沒(méi)辦法向下兼容芹啥。
19、 【推薦】方法放置順序:public-->protected-->private墓怀。一個(gè)類,往往使用者更關(guān)注的是public的虱朵。構(gòu)造方法、重載方法碴犬、雷同方法,按順序放在一起