代碼整潔之道-參數(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)了渗柿。怎樣來(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ò)比較兩處代碼不難看出:

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

關(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ī)則:

  1. 每個(gè) map 中的 code 不能為空,否則跳過(guò)
  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] 中相同的問(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做比較郑口,fromNullableor 顯然更清晰)鸳碧。
還有很重要的一點(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)處理,特別是在遍歷 MapList 之類的集合時(shí)鹤耍,因?yàn)槟阌肋h(yuǎn)不知道調(diào)用者什么時(shí)候會(huì)給你一個(gè)null肉迫。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市稿黄,隨后出現(xiàn)的幾起案子喊衫,更是在濱河造成了極大的恐慌,老刑警劉巖杆怕,帶你破解...
    沈念sama閱讀 211,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件格侯,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡财著,警方通過(guò)查閱死者的電腦和手機(jī)联四,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)撑教,“玉大人朝墩,你說(shuō)我怎么就攤上這事。” “怎么了收苏?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,435評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵亿卤,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我鹿霸,道長(zhǎng)排吴,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,509評(píng)論 1 284
  • 正文 為了忘掉前任懦鼠,我火速辦了婚禮钻哩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘肛冶。我一直安慰自己街氢,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,611評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布睦袖。 她就那樣靜靜地躺著珊肃,像睡著了一般。 火紅的嫁衣襯著肌膚如雪馅笙。 梳的紋絲不亂的頭發(fā)上伦乔,一...
    開(kāi)封第一講書(shū)人閱讀 49,837評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音董习,去河邊找鬼烈和。 笑死,一個(gè)胖子當(dāng)著我的面吹牛阱飘,可吹牛的內(nèi)容都是我干的斥杜。 我是一名探鬼主播,決...
    沈念sama閱讀 38,987評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼沥匈,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蔗喂!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起高帖,我...
    開(kāi)封第一講書(shū)人閱讀 37,730評(píng)論 0 267
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤缰儿,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后散址,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體乖阵,經(jīng)...
    沈念sama閱讀 44,194評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,525評(píng)論 2 327
  • 正文 我和宋清朗相戀三年预麸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了瞪浸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,664評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡吏祸,死狀恐怖对蒲,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤蹈矮,帶...
    沈念sama閱讀 34,334評(píng)論 4 330
  • 正文 年R本政府宣布砰逻,位于F島的核電站,受9級(jí)特大地震影響泛鸟,放射性物質(zhì)發(fā)生泄漏蝠咆。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,944評(píng)論 3 313
  • 文/蒙蒙 一北滥、第九天 我趴在偏房一處隱蔽的房頂上張望刚操。 院中可真熱鬧,春花似錦碑韵、人聲如沸赡茸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,764評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至遗菠,卻和暖如春联喘,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背辙纬。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,997評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工豁遭, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人贺拣。 一個(gè)月前我還...
    沈念sama閱讀 46,389評(píng)論 2 360
  • 正文 我出身青樓蓖谢,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親譬涡。 傳聞我的和親對(duì)象是個(gè)殘疾皇子闪幽,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,554評(píng)論 2 349