如果您有任何結果或輸入無法由您的程序處理的情況下,那應該是一個錯誤。你應該知道你的程序可以處理和只允許。對於未來可能出現的情況,如果結果可以處理,但還沒有結果,我仍然建議將其視爲一個錯誤,直到您真正需要結果爲止。如果有疑問,你不知道,程序不知道,它不能處理,你沒有配備你的程序來處理它,所以這是一個錯誤。該計劃不能做任何事情,只能停下來。
對於用戶輸入來說,依靠程序最終崩潰是一個非常糟糕的主意。你不知道什麼時候,甚至會不會崩潰。它可能只是最終做錯了事,或者做了十件事,然後崩潰,它不應該做,並且如果輸入已被驗證,則不會做。
在防範自己的錯誤方面,更多的是混合包。您需要更多地關注通過測試來確保工作正常,消除未知因素,校對閱讀,確保您確切知道程序的工作方式等。您偶爾還會遇到內部處理可能產生不良結果的情況。
事實證明,對於給定的情況,結果不是錯誤,你不處理異常,你處理null,而不是一個例外。在這種情況下,結果不是錯誤。所以你處理結果而不是錯誤。這不可能更簡單。如果你正在捕捉一個異常,然後做一些可以做的事情,如果這樣做,如:
try
extra = i_need_it(extra_id)
show('extra', extra)
catch
show('add_extra')
然後,這是不對的。如果你沒有額外的東西,你有一個完全可以接受的行動方案。
這是好多了,它讓你的意圖明顯沒有額外的冗長:這裏
Something extra = i_want_it(extra_id)
if extra ==== null
show('add_extra')
else
show('extra', extra)
通知你需要什麼特別的避免受涼從另一層異常。我如何把上面的嘗試抓住是一個不好的做法。你應該只是包裝引發異常的東西:
Something extra
try
extra = i_need_it(extra_id)
if extra === null
show('add_extra')
else
show('extra', extra)
當你這樣的事情,那麼它只是將null轉換爲異常,然後再回來。這是Yo-Yo編碼。直到你實際上是能夠實現處理的空
Object i_need_it(int id) throws
:
你應該開始。如果您能夠實現對異常的處理,則可以實現對null的處理。
當事實證明,事情並不總是需要添加任何此方法或更改i_need_it它(如果爲null總是處理):
Object|null i_want_it(int id)
另一種方法是檢查是首先存在:
bool does_it_exist(int id)
不這樣做,所以經常的原因是因爲它通常出來是這樣的:
if(does_it_exist(id))
Something i_need = i_need_it(id)
這往往更容易出現併發問題,可能需要更多可能不可靠的調用(網絡上的IO),並且可能效率低下(兩個RTT而非一個)。其他調用通常會像這樣合併,如更新(如果存在),插入(如果唯一),更新(如果存在或插入)等,然後返回通常是最初檢查的結果。其中一些在有效載荷大小效率和RTT效率方面存在衝突,其也可以根據多個因素而變化。
然而,當需要根據存在與否存在交替行爲時更便宜,但是您不需要處理它。如果你也不需要擔心上述問題,它會更清晰一些。
你可能甚至想:
void it_must_exist(int id) throws
這又是有用的,因爲如果你只需要確保的東西存在,它往往比得到更便宜。然而,很少有人會需要這樣做,因爲在大多數情況下,您會想知道是否存在某些內容以便直接處理它。
構想它的一種方式是,如果'does_it_exist'只是簡單地在調用堆棧上顯式返回一個布爾值,那麼'i_want_it'是組合'has'和'get'的效果。
雖然有兩種不同的方法更加明確分離方法簽名,有時你可能需要從別的東西和最簡單的方式傳遞下來,如果你不是我的位模糊的是:
Object|null get(int id, bool require) throws
這是更好的,因爲你將合同交給呼叫鏈,而不是建立在遠離行動的沙灘上。有許多方法可以更明確地傳遞你的契約,但它往往會讓人費解YAGNI(IE,傳遞一個方法調用者)。
你應該提前拋出異常,你可以想要安全而不是遺憾,所以誤報是好的。一旦你發現它是一個誤報,儘管如此,你可以在源頭修復它。
你應該極其罕見地處理異常。絕大多數應該擊中頂部,然後調用日誌記錄和輸出處理程序。通過直接傳遞結果並處理它,您可以適當地修復其他問題。當你有許多用途中有一個誤報時,只有這個用法。不要只是刪除根目錄中的檢查,並打破許多其他仍然存在錯誤的情況。
Java是一種不幸的語言,因爲我不能有一種說法不傳遞null的方法,或者這個變量必須是非空的。
當缺少這樣的功能時,通常最好在它們的來源(例如IO)中檢查空值,而不是每次將某個東西傳遞給某個方法時使用。否則,這是一個荒謬的空值檢查。
如果你真的需要這個,你可以應用這個模式來創建函數來替換你的參數檢查ifs。您將與對象替換ID本身如:
Object i_want(Object it) throws
if(it === null)
throw
return it;
然後:
void give_me(Object it)
this.it = Lib<Object>::i_want(it)
一種恥辱沒有中繼類型。
或者:
void i_get_it(Getter<Object> g)
this.it = Lib<Object>::i_want(g.gimme())
你可能有一個特定的吸氣劑,而不是通用的。
或者:
void i_need_it(Result<Object> r)
this.it = r.require()
如果你只能做它的邊界而不是在(當你打電話東西拿出來控制,其中非空的結果不能得到保證的一側),而最好是做它在那裏或任何用法的方法被記錄爲返回null並且只在那裏,因爲這是真正需要的地方,這就意味着當你得到一個null不屬於它的地方時(錯誤發生),你不會去有一個容易的時間找出它來自哪裏。它可以從IO傳來,並且不會產生空指針異常,除非有東西試圖對其進行操作。這些空值可以在系統中傳遞幾天甚至幾個月,作爲等待中止的定時炸彈。
我不會自己這樣做,但在某些情況下,我不會責怪人們實施上面的防禦性編程方法,因爲Java的破壞類型系統默認情況下是鬆散的並且不能被限制。在強類型語言中,除非明確聲明,否則不允許使用null。
請注意,儘管我稱之爲破壞,但幾十年來我一直在使用明顯較寬鬆的語言來構建大型,複雜和關鍵系統,而不必花費過多的檢查來代碼。紀律和能力決定了質量。
假結果或情況發生時,您認爲是所有調用者都無法處理的結果,但至少有一個調用者可以正確處理它。在這種情況下,你不會處理異常,而是給調用者提供結果。這很少有例外。
Java 8具有可選功能,但看起來並沒有什麼幫助。這是內部平臺效應的一個可怕的例子,它試圖將新的語法作爲類實現,並且最終不得不添加一半現有的Java語法,使得它非常笨重。像往常一樣,現代Java OOP通過增加更多功能和更復雜的東西解決了每個人都希望減少欺騙的問題。如果你真的想像那樣鏈接,你可能想嘗試一些例如直接實現該語法的kotlin。
現代語言將意味着你不必擔心極本,只是有:
void i_need_it(Object it)
this.it = it
void i_want_it(Object? it)
this.it = it
現代語言甚至可能回到原來的對象的方法,而不返回(替換爲空自我作爲標準和自動返回),所以人們可以在編程中擁有自己的花哨鏈接或其他任何時髦的東西。
你不能有一個基類的工廠給你一個Null或NotNull,因爲你仍然需要傳遞基類,並且當你說你想要NotNull的時候這將是一個類型違規。
你可能想玩弄方面,靜態分析等,雖然這有點兔子漏洞。所有的文檔都應該指出是否可以返回null(儘管如果是間接的,它可能會被忽略)。
你可以讓一個類如MightHave包裹你的結果,你可以把它放在get,require等方法上,如果你不喜歡靜態但是想吃一個if雖然它也處於輕度複雜的領域並將所有方法簽名搞亂,將遍佈各處的所有東西都捆綁起來,這是一個醜陋的解決方案。作爲替代罕見異常處理的例子,由於調用圖的複雜性和各層中未知數的數量,異常看起來很有用,它只是非常方便。
一個極其罕見的情況是,當你的源代碼知道要拋出什麼異常時,如果它需要拋出但不能傳遞下去(儘管在兩個層之間需要接近警告)。有些人可能也想要這樣做,因爲它可以輕鬆地追蹤丟失項目來自哪裏以及需要哪些項目,這些都是使用檢查可能無法提供的(它們很可能在靠近源的位置失敗,但不能保證)。應該謹慎,因爲這些問題可能比合理多態性,元/動態編碼等問題更能表明流控制不佳或過於複雜。
應該注意事項,如默認值或空對象模式。兩者都可能最終隱藏錯誤,成爲最好的猜測或治癒疾病。通常可以使用NullObject模式和Optional兩種方法來簡單地關閉或雙向傳遞解釋器中的內置錯誤處理。
默認並不總是不好,但可以落入猜測的領域。隱藏來自用戶的錯誤最終將其設置爲失敗。默認(和sanitisation)總是需要仔細考慮。
諸如NullObject模式和Optional之類的東西可以被過度使用以有效地關閉空指針檢查。它只是假設null不會是一個錯誤,它最終會導致程序在做某些事情,而不是其他事情,但你不知道是什麼。在某些情況下,這可能會產生令人捧腹的結果,例如用戶的信用卡爲空。確保你正在使用這些東西,而不是將它們用於將所有代碼簡單地包裝在忽略空指針異常的try catch中的效果。這是非常普遍的,因爲人們傾向於修復引發異常的錯誤。事實上,真正的錯誤往往會更遠。最終會出現一個錯誤的IO處理錯誤返回null,並且null在程序中傳遞。與其修復null的一個來源,人們會嘗試修復它到達的所有地方,從而導致錯誤。
NullObject模式或MagicNoop模式有它們的地方,但不是常用的。你不應該使用它們,直到它變得顯而易見,它們以合理的方式是有用的,不會導致比解決問題更多的問題。有時一個noop實際上是一個錯誤。
ack ...不要捕捉異常...如果除數爲零,該怎麼辦?然後,您還將捕獲除以零的ArithmeticException,但將其視爲「預期的」NullPointerException。儘可能具體說明你想要捕捉的異常情況。 – TofuBeer 2010-07-11 21:27:45
有沒有人想把自己的脖子伸出來,並說if語句比拋出異常要便宜? – AJay 2010-07-12 00:50:32
是的,我喜歡。 'if'語句比拋出異常要便宜得多。 – EJP 2010-07-12 06:37:05