不要再返回Null了

背景

最近在做公司系統(tǒng)的模塊化重構(gòu)豆励,發(fā)現(xiàn)了大量的函數(shù)和方法會在一定情況下返回一個Null值途戒。雖然我能理解當(dāng)時作者一定是想告訴我們此函數(shù)或方法在一定條件下返回空值哀卫。但是真的使用返回Null的方式來表達返回空值真的合適嗎母市?

知識回顧

先從語意上理解什么是Null够坐?

我從WIKIpedia上選了靠前的萄唇、編程角度上來說關(guān)于Null的解釋檩帐。

Null 是一特殊指針值(或是一種 對象引用)表示這個指針并不指向任何的對象。這樣的指針稱之為 Null指針[1]            
                                    
                                                                       --WIKIpedia

計算機專業(yè)出身的同學(xué)應(yīng)該能夠很好理解這段話的意思另萤。意思是說Null的類型是指針(因為它是Null指針)湃密。

在許多定義里,Null 意指 "沒有值" 或是 "未知的值"四敞。  
                                                                --WIKIpedia

這段話則說明了Null在語意上的模糊性泛源。既可能表示“沒有值”,也可能表示“未知的值”忿危。

為什么不要返回Null

  1. 語意的角度

先看一段代碼达箍。這段代碼使用了React 16 里面的新特性。我先不說這段代碼的意思铺厨,我們嘗試只看代碼猜一猜這段執(zhí)行這段代碼以后會發(fā)生什么事情缎玫。

const MAX_PIZZAS = 20;

function addAnotherPizza(state, props) {
  if (state.pizza === MAX_PIZZAS) {
    return null;
  }

  return {
    pizza: state.pizza + 1,
  }
}

this.setState(addAnotherPizza);

在上面的代碼里面, 我們可以知道作者定義了一個常數(shù)MAX_PIZZAS,和一個函數(shù) addAnotherPizza解滓,然后執(zhí)行 React.Component的setState方法赃磨。

好,讀方法名字的時候洼裤,我們大概可以猜測到這是一個更新披薩數(shù)量的的方法邻辉。直意過來就是“添加另一個披薩”。直接看這個函數(shù)的返回值,可以知道這個方法返回了一個對象(新的state)恩沛,這個新的對象更新了pizza的數(shù)量(其實這種命名也不好在扰,應(yīng)該表明是numOfPizza)。但是我們也發(fā)現(xiàn)雷客,當(dāng)pizza數(shù)量達到一個上限的時候芒珠,就會返回一個null。

OK搅裙。想象一下你是一個新來這個項目的員工皱卓,你來猜測一下這里返回一個null代表了什么意思。你可能會說部逮,那肯定就是不再添加pizza的數(shù)量咯娜汁。對一半吧。那為什么返回null而不返回空對象呢兄朋?

在揭曉正確答案之前掐禁,首先說一下什么是語意。我理解的語意就是你寫的變量名颅和,函數(shù)名傅事,方法名,類名峡扩,接口名等等有意義蹭越。

在語言學(xué)上,指發(fā)出訊息者想要表示或傳達給發(fā)現(xiàn)者或接收者的理念教届;
                                                    --wikipedia

也就是說我們編寫的代碼不能只有機器能夠理解响鹃,以后接我們工作的同學(xué)也能輕易地理解我們的代碼到底想表達什么意義。

把上面的的函數(shù)翻譯成人話的話案训,就是:

    A: 再加一個披薩 ( 函數(shù)名: addAnotherPizza)
    B: 當(dāng)前不足20個买置,好的,成功加一個披薩
    ...
    A: 再加一個披薩
    B:當(dāng)前已經(jīng)夠20個萤衰。那么堕义, null。

你能夠理解嗎脆栋?

回到上面的代碼倦卖。React 16版本里面setState接收null值來明確告訴React不要渲染。所以除了不再添加一個披薩到新的state以外椿争,還有一個意思就是告訴React不要做渲染了怕膛。

至少我作為一個新的代碼維護者的角度來說的話,我不能輕易地秦踪、明確地判斷出當(dāng)addAnotherPizza返回null時褐捻,到底想表達什么掸茅。

  1. 防范NPE的角度 (NullPointerException)

有JAVA背景的同學(xué),應(yīng)該對于NPE一點也不陌生柠逞。NPE應(yīng)該是伴隨了我們很多的開發(fā)時間昧狮。NPE主要發(fā)生在調(diào)用一個對象的方法時,檢測到應(yīng)該有的對象引用的值是null板壮。

所以為了防范NullPointerException, 我們?yōu)槲覀兊姆椒ê秃瘮?shù)添加了數(shù)不清的null 檢查邏輯逗鸣。使用null值檢查邏輯防范NullPointerException是一個很好的編程習(xí)慣。這可以增強系統(tǒng)的健壯性绰精。

但是撒璧,如果你仔細觀察我們的代碼,可以發(fā)現(xiàn)很多的null檢查是不必要的笨使。特別是對于一些自建系統(tǒng)的接口函數(shù)調(diào)用卿樱。舉個如下的列子:

class Member {
    private String phoneNumber;
    private String memberId;

    public String getPhoneNumber(String memberId){
        if ( memberId == this.memberId ) {
            return this.phoneNumber;
        } else {
            return null
        }
    }
}

相信大家在很多的代碼里面看見過類似的邏輯。即當(dāng)成功時返回某項值硫椰,反之則返回null繁调。

從語意的角度上說的話,你找某個管理員去詢問某個member的電話號碼靶草,得到null的回饋時涉馁,你會怎么想?是去想這個member沒有電話號碼呢爱致?還是沒有這個member,還是這個管理員傻了寒随?

除了語意上的模凌兩可以外糠悯,在一個系統(tǒng)里面返回過多的null,也在一定程度上增加了null的檢查邏輯代碼數(shù)量妻往,其實就是給系統(tǒng)增加了不必要的復(fù)雜性互艾。所以,如果在某些情況下讯泣,比如上述返回電話號碼的情況下纫普,為何不返回一個空字符串呢?

從防范NPE的角度來看好渠,返回空字符串的話昨稼,首先,getPhoneNumber的調(diào)用者是不需要進行null檢查的拳锚。因為你不返回null假栓,那為什么還要進行null檢查?其次霍掺,主動不去返回null還有一個好處匾荆,就是我們作為一個有素養(yǎng)的程序員和工程師拌蜘,主動從系統(tǒng)健壯性的角度防范了NPE的產(chǎn)生,從源頭上保護了NPE對系統(tǒng)帶來的危害牙丽。

不給系統(tǒng)留下任何健壯性危害隱患简卧,是作為一個有素養(yǎng)的程序員應(yīng)該有的覺悟和編程習(xí)慣。作為一個有素養(yǎng)的程序員烤芦,永遠不要指望Debug來讓我們的系統(tǒng)健壯举娩,而是時時刻刻地去主動考慮如何才能保護我們的系統(tǒng)不因為我們技術(shù)的原因而崩塌。

  1. 面向?qū)ο缶幊痰慕嵌?/h3>

在從面相對象編程的角度來展開討論之前拍棕,我們有必要知道什么是面相對象編程晓铆。

Object-oriented programming (OOP) is a programming paradigm based on the concept of "objects", which may contain data, in the form of fields, often known as attributes; and code, in the form of procedures, often known as methods.
                                                    --wikipedia

說白一點就是在系統(tǒng)里一切皆對象,對象包含了一切必要的屬性和屬性操作方法绰播。

那么我們的系統(tǒng)就是由對象和對象的關(guān)系構(gòu)成的骄噪。在上面的知識回顧當(dāng)中,我們可以知道null是一個指針類型的值蠢箩。表示該指針不指向任何的對象链蕊。從一個系統(tǒng)抽象的角度來看,指針類型屬于比較底層的類型谬泌。那么在我們的很多方法當(dāng)中去返回一個底層的類型值是否合適呢滔韵?

舉個簡單的列子,我們?nèi)耸怯杉毎M成器官然后再組成我們?nèi)诉@個類型的掌实。如果每一個細胞代表了一個實例陪蜻,那么每個實例所在的位置則由指針的值保存(實例內(nèi)存地址)。我們?nèi)诉@個系統(tǒng)中贱鼻,細胞和細胞有關(guān)系宴卖,器官與器官有關(guān)系,器官與細胞有關(guān)系邻悬。他們互相有數(shù)據(jù)的來往症昏,有數(shù)據(jù)的處理,但是唯獨沒有的是父丰,當(dāng)某個器官想查詢某個細胞狀態(tài)時肝谭,相應(yīng)器官返回一個空位置(null)的指針。因為這樣既沒有意義蛾扇,也沒有任何人體內(nèi)部器官會去做這個事情攘烛。

人體是一個非常健壯和復(fù)雜的系統(tǒng),我們可以通過觀察我們?nèi)梭w自身的系統(tǒng)來獲取編程的靈感镀首。

null在我們的編程過程中医寿,很多時候被誤用為表明想要查找的對象不存在。如果這么想的話蘑斧,只能說明你還離OOP還有一段距離靖秩。因為你還在用機器的思維在思考編寫代碼须眷。如果嚴(yán)格按照OOP來思考如何構(gòu)建我們系統(tǒng)的話,我們就會有更好的方法去表明一個不存在的對象(Clean Code: Special Case Pattern)沟突。

業(yè)界相關(guān)討論

以上都是我自己對返回null值的思考和總結(jié)花颗。在業(yè)界上,早就有相關(guān)是否返回null值有激烈的討論惠拭。沒有討論出業(yè)界一致公認的標(biāo)準(zhǔn)扩劝。

在Stackoverflow上關(guān)于是否返回null的討論: Is returning null bad design?

Null之父Tony Hoare關(guān)于Null值的文章: Null References: The billion dollar mistakes

Clean Code 作者 Robert C. Martin 關(guān)于是否返回null值的看法是 “Avoid returning null”.

其他一些作者關(guān)于是否返回null的一些討論: Why NULL is Bad?

總結(jié)

本文認為Null值不應(yīng)該由程序員在方法中返回,因為這樣做职辅,一是違背了方法名的語意棒呛。二是由于我們返回null值,更加增加了系統(tǒng)的不穩(wěn)定性域携;因為在調(diào)用者忘記做null檢查時簇秒,就一定會因為我們的方法出錯。最后秀鞭,null值所充斥的系統(tǒng)模型違反了OOP原則趋观。

所以,本文認為锋边,在我們構(gòu)建的系統(tǒng)中皱坛,不應(yīng)該有返回null值的方法和函數(shù)存在。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末豆巨,一起剝皮案震驚了整個濱河市剩辟,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌往扔,老刑警劉巖抹沪,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異瓤球,居然都是意外死亡,警方通過查閱死者的電腦和手機敏弃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進店門卦羡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人麦到,你說我怎么就攤上這事绿饵。” “怎么了瓶颠?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵拟赊,是天一觀的道長。 經(jīng)常有香客問我粹淋,道長吸祟,這世上最難降的妖魔是什么瑟慈? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮屋匕,結(jié)果婚禮上葛碧,老公的妹妹穿的比我還像新娘。我一直安慰自己过吻,他們只是感情好进泼,可當(dāng)我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著纤虽,像睡著了一般乳绕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上逼纸,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天洋措,我揣著相機與錄音,去河邊找鬼樊展。 笑死呻纹,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的专缠。 我是一名探鬼主播雷酪,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼涝婉!你這毒婦竟也來了哥力?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤墩弯,失蹤者是張志新(化名)和其女友劉穎吩跋,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體渔工,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡锌钮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了引矩。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片梁丘。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡笤虫,死狀恐怖毙沾,靈堂內(nèi)的尸體忽然破棺而出顶别,到底是詐尸還是另有隱情兽掰,我是刑警寧澤翎苫,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布师脂,位于F島的核電站乡数,受9級特大地震影響晚唇,放射性物質(zhì)發(fā)生泄漏织盼。R本人自食惡果不足惜杨何,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一酱塔、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧晚吞,春花似錦延旧、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至捌蚊,卻和暖如春集畅,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背缅糟。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工挺智, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人窗宦。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓赦颇,卻偏偏與公主長得像,于是被迫代替她去往敵國和親赴涵。 傳聞我的和親對象是個殘疾皇子媒怯,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 第2章 基本語法 2.1 概述 基本句法和變量 語句 JavaScript程序的執(zhí)行單位為行(line),也就是一...
    悟名先生閱讀 4,148評論 0 13
  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時...
    歐辰_OSR閱讀 29,383評論 8 265
  • 1.你知道嗎髓窜?太陽每一天升起的方位都不一樣扇苞。如果你先吃下去,你就會發(fā)現(xiàn)寄纵。日出日落并不是簡單的東升西落鳖敷。 2.緩坡,...
    夜寂日志閱讀 284評論 0 0
  • 如果掙斷繩索程拭,我該去哪兒定踱? 遠處,有無數(shù)的地方可去恃鞋;卻又都不能去… 我不知道崖媚,為什么每每溫?zé)釟堄啵湎夹睊斓臅r候山宾,...
    左菊秋華閱讀 299評論 0 1
  • 1 早上我妹妹來找我阶祭,她拿著一本很厚的區(qū)塊鏈書绷杜,說看了幾頁就不想看了直秆,實在是晦澀難懂,問我到底區(qū)塊鏈?zhǔn)鞘裁幢廾恕⒈忍貛?..
    貓曉語閱讀 305評論 0 0