原創(chuàng)文章舞蔽,轉(zhuǎn)載請(qǐng)注明出處
引言
代碼質(zhì)量的好壞荣病,本身是一個(gè)比較難量化的標(biāo)準(zhǔn),現(xiàn)在應(yīng)該很少有公司再以一個(gè)程序員產(chǎn)出的代碼行數(shù)作為標(biāo)準(zhǔn)了渗柿。怎樣來(lái)評(píng)判代碼的好壞其實(shí)是一項(xiàng)比較麻煩的事情个盆,每個(gè)人的著眼點(diǎn)不同,相應(yīng)的代碼就各式各樣朵栖。但是根據(jù)自身經(jīng)驗(yàn)來(lái)看颊亮,當(dāng)然也是我比較信奉的一點(diǎn),就是在目前的開(kāi)發(fā)條件下陨溅,代碼的組織結(jié)構(gòu)是比執(zhí)行效率需要優(yōu)先考慮的事情终惑。
本篇的主旨是整理一些在開(kāi)發(fā)中能夠?qū)嶋H提升代碼質(zhì)量的工具和技巧。
Apache-Commons和Guava
相信有過(guò)一些開(kāi)發(fā)經(jīng)驗(yàn)的人對(duì)這兩個(gè)工具都不會(huì)太陌生门扇,他們提供的許多類能夠大大提高開(kāi)發(fā)效率雹有,并且使代碼更加整潔偿渡。個(gè)人更喜歡 Guava 的設(shè)計(jì),工作中往往是混用霸奕,當(dāng)然要理解每種工具的適用場(chǎng)景溜宽,才能做到高效和簡(jiǎn)潔。
1. 參數(shù)檢查
剛參加工作的時(shí)候质帅,印象非常深刻的是當(dāng)我想檢查一些參數(shù)是否符合要求時(shí)适揉,要寫(xiě)許多冗余的代碼。
例如在一個(gè) Service 的入口處想要檢查一個(gè) String 類型的參數(shù)是否為空煤惩,這是我最初的寫(xiě)法:
public void doSomething(String str) throws Exception {
if (str == null || str.length() == 0) {
throw new Exception("str should not be blank");
}
}
乍看之下確實(shí)沒(méi)有什么問(wèn)題嫉嘀,但是當(dāng)參數(shù)個(gè)數(shù)增加的時(shí)候事情就變得麻煩了,同樣的代碼邏輯需要重復(fù)好幾遍魄揉,最致命的就是導(dǎo)致可讀性下降吃沪。還有就是當(dāng)str
是連續(xù)的空格時(shí)也繞過(guò)了檢查。
Guava中的 Preconditions類就是專門解決參數(shù)檢查問(wèn)題的什猖,先來(lái)看看使用了 Preconditions 以后的代碼變成了什么樣子:
public void doSomething(String str) throws Exception {
Preconditions.checkArgument(StringUtils.isNotBlank(str), "str should not be blank");
}
很多教程中都強(qiáng)烈建議將 Preconditions 靜態(tài)導(dǎo)入票彪,這樣代碼會(huì)更加簡(jiǎn)潔。通過(guò)比較兩處代碼不難看出:
- 代碼的可讀性提高了
- 替代掉了
if
和throw
語(yǔ)句不狮,提升了可維護(hù)性 - 省略了判斷條件降铸,代碼更健壯
關(guān)于2,3點(diǎn)可能需要說(shuō)明一下摇零。記得 Boss 之前有跟我聊過(guò)一次推掸,有一點(diǎn)是說(shuō)自己寫(xiě)的代碼里應(yīng)該盡量少出現(xiàn) if
這樣的控制結(jié)構(gòu),當(dāng)時(shí)理解并不是太深刻驻仅,但是隨著寫(xiě)的代碼越來(lái)越多谅畅,漸漸意識(shí)到這其實(shí)是思想的一種轉(zhuǎn)變。
在一個(gè)方法之中噪服,出現(xiàn)的控制結(jié)構(gòu)越多毡泻,思維還停留在面向過(guò)程編程的可能性就越大(雖然現(xiàn)在大多數(shù)程序員都不愿意承認(rèn)這點(diǎn))。就以上面的代碼為例粘优,判斷字符串是否為空的這項(xiàng)功能并不是方法的主要業(yè)務(wù)仇味,那么就應(yīng)該由專門做這項(xiàng)功能的類(對(duì)象)來(lái)進(jìn)行處理,所以我們交給了 StringUtils
雹顺,同樣的丹墨,根據(jù)參數(shù)是否滿足要求來(lái)拋出異常的功能我們交給了 Preconditions
。
順帶值得一提的是 Preconditions
中還有許多檢查參數(shù)的方法:checkNotNull()
, checkState()
等等嬉愧,這些方法都是快速失敗的贩挣。
2. 思考 null
想表達(dá)的意義
每次自己寫(xiě)的程序報(bào)了空指針錯(cuò)誤,我都會(huì)提醒自己更加謹(jǐn)慎,因?yàn)檫@其實(shí)是在說(shuō):
你考慮的不夠周全
許多時(shí)候NPE 所代表的問(wèn)題是我們并沒(méi)有思考清楚 null
在這里是想表達(dá)什么意思王财,還是先看一個(gè)例子:
我們現(xiàn)在想從一個(gè) List<Map<String, String>>
結(jié)構(gòu)的鏈表中逐個(gè)解析每個(gè)字段卵迂,他的數(shù)據(jù)大概是這個(gè)樣子:
[
{
"code":"123",
"name":"yzhang",
"nickname":"Rocket"
},
{
"code":"234",
"name":"Kevin",
"nickname":"Bee"
}
]
但是現(xiàn)在有一些規(guī)則:
- 每個(gè) map 中的
code
不能為空,否則跳過(guò) -
name
可以為空搪搏,但是不能顯示為“null” -
nickname
如果為空狭握,則使用默認(rèn)值“Avenger”
下面是一段實(shí)現(xiàn)代碼:
for (int i = 0, size = personList.size(); i < size; i++) {
Map<String, String> person = personList.get(i);
if (MapUtils.isNotEmpty(person)) {
String code = person.get("code");
String name = person.get("name");
String nickname = person.get("nickname");
if (StringUtils.isBlank(code)) continue;
if (name == null) name = "";
if (nickname == null) nickname = "Avenger";
// do something else
}
}
這段代碼已經(jīng)考慮了一些可能出現(xiàn) NPE 的情況闪金,但是出現(xiàn)了和 [1] 中相同的問(wèn)題疯溺,有大量的if
語(yǔ)句,對(duì)于后期維護(hù)非常不利哎垦。
Guava 中提供了Optional
類來(lái)強(qiáng)制開(kāi)發(fā)人員思考 null
所表達(dá)的意義囱嫩,正如 Optional 表達(dá)的意思,當(dāng)一個(gè)變量可能會(huì)出現(xiàn) NPE 時(shí)我們就應(yīng)該用 Optional
來(lái)處理它(Java 8的util包已經(jīng)加入了 Optional
類漏设,接口命名上有稍許不同)
看看改進(jìn)后的代碼:
for (int i = 0, size = personList.size(); i < size; i++) {
Map<String, String> person = Optional.fromNullable(personList.get(i)).or(Maps.newHashMap());
String code = person.get("code");
if (StringUtils.isBlank(code)) continue;
String nickname = Optional.fromNullable(person.get("nickname")).or("Avenger");
String name = Optional.fromNullable(person.get("name")).or("");
// do something else
}
同樣的墨闲,我們減少了很多 if
結(jié)構(gòu),代碼也更便于閱讀(比起與一堆null
做比較郑口,fromNullable
和 or
顯然更清晰)鸳碧。
還有很重要的一點(diǎn),即使在第一段代碼中犬性,也有不少人會(huì)忘記寫(xiě)if (MapUtils.isNotEmpty(person))
瞻离,因?yàn)檫@沒(méi)有在三點(diǎn)要求以內(nèi)。
關(guān)于 Optional
的詳細(xì)使用可以參考 Guava 的官方文檔乒裆,值得注意的是Optional.of(T t)
也是快速失敗的套利。建議在一個(gè)對(duì)象有不確定性的時(shí)候都考慮使用 Optional 來(lái)處理,特別是在遍歷 Map
和 List
之類的集合時(shí)鹤耍,因?yàn)槟阌肋h(yuǎn)不知道調(diào)用者什么時(shí)候會(huì)給你一個(gè)null
肉迫。