1. 程式人生 > >null?對象?異常?到底應該如何返回錯誤信息

null?對象?異常?到底應該如何返回錯誤信息

數據 基本 容易 滿足 定位 sage get 沒有 java

這篇文章記錄我的一些思考。在工作了一段時間之後。

問題的核心很簡單:到底如何返回錯誤信息。

學生時代,見到過當時的老師的代碼:

1 if (foo() == null) {
2 
3 }

當然,這位老師是一位比較擅長c/c++的老程序員,所以他的代碼其實使用c寫的。但是意思和這段代碼類似。當時,我很好奇為什麽要對一個方法的返回值是不是null進行判斷。現在當然很清楚了:在很多win32的API裏面,是通過返回值為null來傳遞“函數調用失敗”這一種信息的。

那麽,這麽做好嗎?

我翻看了很多的博客,大致上說這種寫法不好的占多數。最重要的理由如下:

  • 無法展示更詳細的錯誤信息
  • 容易讓調用者忽略

這種說法基本還是有道理的。因為返回值為null,確實很明顯得存在這兩種問題。

對於第一點,其比較關鍵的影響就是,既然不知道更多信息,也就無法在日誌中體現。從而不能針對可能出現的多種異常進行不同的日誌打印和處理。曾經也發生過因為缺失了日誌打印,導致花很多時間來定位一個問題的情況。尤其是線上環境不比本地隨意debug,一個簡單的問題經過幾次這種“信息丟失”之後,就有可能成為一個“疑案”。

而第二點,更是嚴重影響系統安全性、穩定性。人都有不小心的時候,如果忘了對某個接口的返回值進行校驗,就會導致代碼邏輯是按照“這件事已經發生並且正確發生”寫的,但是實際執行過程中發生了錯誤。一方面比較難排查,第二方面是可能導致一個問題在另外的地方爆發出來。

那麽是不是絕對不能這麽做呢?其實也不盡然。簡單來說,如果調用方在系統內部、代碼執行邏輯可控、在返回null之前有正確的日誌打印、調用方對這個異常確實沒有辦法處理。滿足這幾個條件就可以使用返回null。但是還是具有危險性。

這個問題的改進方案是返回一個對象。比如寫一個Result對象。

 1 public class Result {
 2 
 3     private Integer code;
 4 
 5     private String message;
 6 
 7     private Object data;
 8     
 9     // getter and setter
10 11 }

這樣就有一個優化:能夠返回錯誤的信息和一些數據。本次方法成功了嗎?如果失敗需要打印什麽信息?如果成功,是不是需要一些內容?

但是這種寫法也有問題。

最主要的是其適用性問題。就是說如果很多方法使用同一個Result,能保證都能通過這種方式返回其需要的數據嗎?一些特別的方法返回的東西不能夠使用一個Object進行轉化。(或者說這樣做代價不小)

其次,很多代碼業務邏輯極其簡單,如果強行使用結果類進行封裝,反倒有畫蛇添足之嫌。

最後,如果要嚴格使用這個類,代碼開發的代價很大。

那麽這種做法適合什麽場景呢?一句話:對外接口。一方面我們要按照一個統一的規則去返回信息(正確或者錯誤)。第二方面,很難要求調用方去捕獲我們的異常。如果他們忘了捕獲,是不是就有可能會造成更嚴重的情況?所以,這種情況下,我們需要告知調用方失敗信息。但是又不能要求對方做出多少改變。

最後一種解決方案是拋異常。

Effective Java裏面用一句話解釋什麽時候需要拋異常,什麽時候不需要。“當你對異常什麽也做不了的時候,就不要拋異常”(大意,有可能記錯了)。

、、、、、、、、未完待續

null?對象?異常?到底應該如何返回錯誤信息