總述
細數(shù)過來,已經(jīng)參加了不少CodeReview, 雖有開發(fā)規(guī)約的指引捉超,但在Review的過程中,還是會有不少問題暴露出來唯绍,本文會總結(jié)在CodeReview之前拼岳,有哪些可以先自查的點,更好的保證代碼的健壯性况芒。
代碼結(jié)構(gòu)
在CodeReview之前惜纸,我們先對代碼結(jié)構(gòu)做一次剖析,開頭绝骚,我們先從最本質(zhì)的面向?qū)ο笳f起耐版,面向?qū)ο螅袑傩云け冢蟹椒ㄍ指缓笪覀儗傩宰隽撕芏嗟男揎棧热缂觭tatic 聲明為靜態(tài)蛾魄,加 final 聲明為常量虑瀑,加 private 聲明為私有。同樣滴须,我們也會對方法做很多修飾舌狗,然后會在方法中調(diào)用別的方法,因為扔水,在真正討論CodeReview之前痛侍,我們得先討論下代碼結(jié)構(gòu),如下所示,一個文件中主届,是分為以下幾大模塊
import xxxx
public class XiaoDao{
public 類型 屬性名
public 回參 方法名(入?yún)?{
一段處理邏輯
調(diào)用其他的方法
一段邏輯處理
返回
}
}
要有了上面大致的結(jié)構(gòu)后赵哲,我們就可以逐段的去自檢。
代碼CR
當提到代碼CR的時候君丁,那一定是分兩大部分枫夺, 一部分是代碼層面,即import是否合理绘闷,屬性/方法修飾是否合理橡庞,工具類使用是否合理。第二部分則是看業(yè)務(wù)邏輯印蔗,即寫的代碼有沒有完成即定的產(chǎn)研需求扒最。本文也會從這兩個方面和大家一起探討如何自檢
代碼層面的自檢
代碼層面的自檢,主要是上面所列的代碼結(jié)構(gòu)是否是符合現(xiàn)有的業(yè)務(wù)华嘹,代碼場景吧趣,本文將以兩份代碼做一個對比
import引包
import這個區(qū)域經(jīng)常會被我們忽略掉,因為在新引入一個類的時候 除呵,IDEA會幫我們自動引入再菊,但是當我們刪代碼的時候爪喘,又不會幫我們自動刪 除,所以這時候就有了多余的引用颜曾,如下所求:
如上圖所求,對一個數(shù)據(jù)結(jié)構(gòu)秉剑,我們開始想的是用List存泛豪,后來還是用Map存,然后把List的聲明代碼給注釋掉了侦鹏,但是上面的import并沒有刪掉诡曙,同樣的情況還可能出現(xiàn)在 StringUtils上面,有團隊自己封裝的略水,有引的Spring里面的价卤,有引的Apache中的,就會在import區(qū)域渊涝,留下很多無用的引用慎璧。
代碼格式化
格式化這個事,說大不大跨释,說小不小胸私,就是順手的一個事,但是有個點要特別注意鳖谈,只格式化自己修改的那一塊代碼岁疼,切記不要全選,不要格式化別人的代碼@峦蕖捷绒!
格式化之前:
格式化之后:
這是一個小細節(jié)瑰排,可能我們寫public會寫的比較順手,或者在開發(fā)的時候暖侨,把一個公用方法凶伙,做了定制化改造,或者是把一個私有方法做了通用的抽象等等它碎,在跑了代碼邏輯沒問題之后,可能就忽略這些小細節(jié)了函荣,但一個定制化的方法,如果聲明成了public扳肛,其他開發(fā)的小伙伴在不知情的情況下調(diào)用了錯誤的邏輯傻挂,就有可能引發(fā)一些缺陷。如下圖所求
日志
日志這個東西是很難打的一個東西挖息,打少了金拒,無法定位問題,打多了呢套腹,又有太多的無用信息绪抛。就本文而言,要檢查幾個必打的地方
入?yún)⒌缳鳎{(diào)外域接口前的參數(shù)幢码,調(diào)外域接口返回的參數(shù),進行一大段復(fù)雜處理邏輯之后的結(jié)果尖飞,有異常之后的信息症副,準備返回給上層的返回值,如下所示:
public class Main {
private static final Logger logger = LoggerFactory.getLogger(Main.class);
public static void main(String[] args) {
logger.info("Main,main param={}", JSON.toJSONString(args));
// 經(jīng)過一系列的復(fù)雜的運算得到了param值
String param = "XiaoDao";
logger.info("Main,main,key param={}", param);
try {
String result = fakeInterface(param);
logger.info("Main,main,fakeInterface param={},result={}", param, result);
} catch (Exception e) {
String msg = String.format("Main,main,fakeInterface,error param=%s", param);
logger.error(msg, e);
}
// 如果有返回值的話政基,還要記錄返回值
logger.info("Main,main result={}", "result");
}
/**
* 假裝這是外域的接口
* @param param
* @return
*/
private static String fakeInterface(String param) {
return "fake" + param;
}
}
循環(huán)
可以說贞铣,我們在寫代碼的時候,十行有六行都是在對集合(List,Map)處理沮明,如果一個方法中出現(xiàn)對一個集合多次遍歷的話辕坝,就要注意了,是不是可以合并在一起荐健,如下所示:
public static void main(String[] args) {
List<TestModel> modelList = Collections.singletonList(new TestModel());
// 獲取NameList
List<String> nameList = modelList.stream().map(TestModel::getName).collect(Collectors.toList());
// 獲取AgeList
List<Integer> ageList = modelList.stream().map(TestModel::getAge).collect(Collectors.toList());
////////// 考慮是否可以用下面一個循環(huán)代替 //////////
List<String> names = new ArrayList<>(modelList.size());
List<Integer> ages = new ArrayList<>(modelList.size());
for (TestModel model : modelList) {
names.add(model.getName());
ages.add(model.getAge());
}
}
業(yè)務(wù)CR
發(fā)現(xiàn)有些小伙伴在進行CR的時候酱畅,不知道要講什么,直接講代碼吧摧扇,沒有一個業(yè)務(wù)前景圣贸,聽的小伙伴也比較懵,講業(yè)務(wù)吧扛稽,又不知道講哪些合適吁峻。這時候,可以參考本文的表述:
在這次的迭代中,我負責(zé)的模塊是XXX,其中主要的邏輯是XXXXXXX用含,涉及到XXX表變更矮慕,XXX配置項變更,XXX定時任務(wù)變更啄骇。如涉及到多個配置項和定時任務(wù)痴鳄,可提前列一個表格出來。
然后開始從代碼入口處缸夹,開始展示代碼痪寻,就是把上述的代碼結(jié)構(gòu)中的每一部分講解清楚∷洳眩可參考以下話術(shù):
現(xiàn)在我有XXXX信息橡类,要經(jīng)過XXXX的處理,得到XXXX結(jié)果芽唇。
然后要調(diào)用XXXX方法顾画,得到XXXX結(jié)果,然后對XXXX結(jié)果進行XXXX的處理匆笤,得到XXXXX
最后完成了XXXX業(yè)務(wù)研侣。
我相信上述這么一段話,在大家開發(fā)的時候炮捧,一直回響在大家腦海中庶诡,CR只不過是把這些話稍加整理,再表述出來給大家聽寓盗。參會的小伙伴也可以順著講的業(yè)務(wù)線灌砖,了解這一塊的業(yè)務(wù)璧函,在串數(shù)據(jù)流的時候傀蚌,也能發(fā)現(xiàn)一些處理欠缺可能引起風(fēng)險的地方。
總結(jié)
上述梳理蘸吓,可以大家平時用以自我Review代碼善炫,我一直相信,寫代碼是一件創(chuàng)造藝術(shù)品的過程库继,是一件需要不斷打磨的過程箩艺,不是說完成了業(yè)務(wù)功能就可以了,要不斷的抽象宪萄,整理艺谆,沉淀才能寫出健壯的代碼!大家一起加油0萦ⅰ静汤!