代碼整潔之道-參數(shù)檢查和 null

原創(chuàng)文章,轉(zhuǎn)載請(qǐng)注明出處

引言
代碼質(zhì)量的好壞,本身是一個(gè)比較難量化的標(biāo)準(zhǔn),現(xiàn)在應(yīng)該很少有公司再以一個(gè)程序員產(chǎn)出的代碼行數(shù)作為標(biāo)準(zhǔn)了。怎樣來評(píng)判代碼的好壞其實(shí)是一項(xiàng)比較麻煩的事情,每個(gè)人的著眼點(diǎn)不同,相應(yīng)的代碼就各式各樣。但是根據(jù)自身經(jīng)驗(yàn)來看,當(dāng)然也是我比較信奉的一點(diǎn),就是在目前的開發(fā)條件下,代碼的組織結(jié)構(gòu)是比執(zhí)行效率需要優(yōu)先考慮的事情。

本篇的主旨是整理一些在開發(fā)中能夠?qū)嶋H提升代碼質(zhì)量的工具和技巧。

Apache-Commons和Guava

相信有過一些開發(fā)經(jīng)驗(yàn)的人對(duì)這兩個(gè)工具都不會(huì)太陌生,他們提供的許多類能夠大大提高開發(fā)效率,并且使代碼更加整潔。個(gè)人更喜歡 Guava 的設(shè)計(jì),工作中往往是混用,當(dāng)然要理解每種工具的適用場(chǎng)景,才能做到高效和簡(jiǎn)潔。

1. 參數(shù)檢查

剛參加工作的時(shí)候,印象非常深刻的是當(dāng)我想檢查一些參數(shù)是否符合要求時(shí),要寫許多冗余的代碼。
例如在一個(gè) Service 的入口處想要檢查一個(gè) String 類型的參數(shù)是否為空,這是我最初的寫法:

public void doSomething(String str) throws Exception {
    if (str == null || str.length() == 0) {
        throw new Exception("str should not be blank");
    }
}

乍看之下確實(shí)沒有什么問題,但是當(dāng)參數(shù)個(gè)數(shù)增加的時(shí)候事情就變得麻煩了,同樣的代碼邏輯需要重復(fù)好幾遍,最致命的就是導(dǎo)致可讀性下降。還有就是當(dāng)str是連續(xù)的空格時(shí)也繞過了檢查。
Guava中的 Preconditions類就是專門解決參數(shù)檢查問題的,先來看看使用了 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)潔。通過比較兩處代碼不難看出:

  1. 代碼的可讀性提高了
  2. 替代掉了 ifthrow 語(yǔ)句,提升了可維護(hù)性
  3. 省略了判斷條件,代碼更健壯

關(guān)于2,3點(diǎn)可能需要說明一下。記得 Boss 之前有跟我聊過一次,有一點(diǎn)是說自己寫的代碼里應(yīng)該盡量少出現(xiàn) if 這樣的控制結(jié)構(gòu),當(dāng)時(shí)理解并不是太深刻,但是隨著寫的代碼越來越多,漸漸意識(shí)到這其實(shí)是思想的一種轉(zhuǎn)變。
在一個(gè)方法之中,出現(xiàn)的控制結(jié)構(gòu)越多,思維還停留在面向過程編程的可能性就越大(雖然現(xiàn)在大多數(shù)程序員都不愿意承認(rèn)這點(diǎn))。就以上面的代碼為例,判斷字符串是否為空的這項(xiàng)功能并不是方法的主要業(yè)務(wù),那么就應(yīng)該由專門做這項(xiàng)功能的類(對(duì)象)來進(jìn)行處理,所以我們交給了 StringUtils ,同樣的,根據(jù)參數(shù)是否滿足要求來拋出異常的功能我們交給了 Preconditions。
順帶值得一提的是 Preconditions 中還有許多檢查參數(shù)的方法:checkNotNull(), checkState()等等,這些方法都是快速失敗的。

2. 思考 null 想表達(dá)的意義

每次自己寫的程序報(bào)了空指針錯(cuò)誤,我都會(huì)提醒自己更加謹(jǐn)慎,因?yàn)檫@其實(shí)是在說:
你考慮的不夠周全
許多時(shí)候NPE 所代表的問題是我們并沒有思考清楚 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ī)則:

  1. 每個(gè) map 中的 code 不能為空,否則跳過
  2. name 可以為空,但是不能顯示為“null”
  3. 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] 中相同的問題,有大量的if語(yǔ)句,對(duì)于后期維護(hù)非常不利。
Guava 中提供了Optional類來強(qiáng)制開發(fā)人員思考 null所表達(dá)的意義,正如 Optional 表達(dá)的意思,當(dāng)一個(gè)變量可能會(huì)出現(xiàn) NPE 時(shí)我們就應(yīng)該用 Optional 來處理它(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做比較,fromNullableor 顯然更清晰)。
還有很重要的一點(diǎn),即使在第一段代碼中,也有不少人會(huì)忘記寫if (MapUtils.isNotEmpty(person)),因?yàn)檫@沒有在三點(diǎn)要求以內(nèi)。
關(guān)于 Optional的詳細(xì)使用可以參考 Guava 的官方文檔,值得注意的是Optional.of(T t)也是快速失敗的。建議在一個(gè)對(duì)象有不確定性的時(shí)候都考慮使用 Optional 來處理,特別是在遍歷 MapList 之類的集合時(shí),因?yàn)槟阌肋h(yuǎn)不知道調(diào)用者什么時(shí)候會(huì)給你一個(gè)null。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容