防禦性程式設計以及我的一些感想

edithfang發表於2014-06-16

防禦性程式設計小例,最開始我是關注了這個人的微信,才看到這篇文章的,最近也工作了一段時間了~對自己以及別人寫的程式碼有了一些新的想法(因為我遇到過很多坑啊囧)。因此,本文來談談這個話題。

在公司,我們碰到的很大一部分問題都是NullPointerException。我常常就想:這段程式明明在我手機上執行好好的,為什麼會出現這種情況呢?

因為,我們永遠都無法預測使用者使用 App 時會發生的各種情況。所以防禦性程式設計可以讓我們減少很大一部分錯誤。

先說一個故事

先來說一個我去年面試過的問題,面試官問我:請你用最熟悉的語言寫一個 atoi 程式。

我心裡一想:這麼簡單!!我要好好寫!不要寫出 bug!我馬上就寫好了,並且用“12334”這種簡單的字串試了又試,沒問題就交給他看。

public int atoi (String a){
    int len = a.length ();
    int num = 0;
    for(int i = len - 1; i >= 0; i--) {
        num += (a.charAt (i) - '0') * Math.pow (10, len - i - 1);
    }
    return num;
}

面試官一看就問我,你找找有什麼問題沒,我看了好幾遍(我都在找 bug),說沒問題啊!我又仔細一想:如果傳進來一個負數怎麼辦呢?比如“-12333”,這段程式就錯了!

我就和麵試官說了~他說嗯,還有問題嗎?我心裡想還有啊?想了幾遍都想不出!我說:效率問題?他不給面子直接就說:你先別管效率!

他說如果我傳進來一個null會怎麼樣?我恍然大悟!!!我有太多東西沒考慮到(我圖樣圖森破啊囧!!)!

上面就是我的一個真實的故事~不知你看了有什麼感想,反正我覺得這次面試可以讓我反思很多我存在的問題。

自從看了前面提到的那篇文章後,我現在寫程式碼就時刻裝著“防禦性程式設計”這 5 個字。

那麼怎麼寫防禦性程式碼呢?

請看Integer.parseInt (String)這個方法,好好看!我現在分析一下~JDK 的大神們怎麼寫健壯的程式碼!(如果有錯誤請指正~ =.=)

public static int parseInt (String string, int radix) throws NumberFormatException {
    if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) {
        throw new NumberFormatException ("Invalid radix: " + radix);
    }
    if (string == null) {
        throw invalidInt (string);
    }
    int length = string.length (), i = 0;
    if (length == 0) {
        throw invalidInt (string);
    }
    boolean negative = string.charAt (i) == '-';
    if (negative && ++i == length) {
        throw invalidInt (string);
    }

    return parse (string, i, radix, negative);
}

上面這段程式碼出自 java 1.7.0_51 的java.lang.Integer類,JDK 開發大神是如何寫程式碼的呢?

可以看到這段程式碼最開始的一部分就是在驗證每個引數的正確性(程式碼中 radix 表示進位制數),這裡最小的進位制就是2,最大進位制是 36。如果進位制數不滿足要求,直接丟擲異常。

然後判斷傳入字串是否為null,如果字串不為null,然後可以取字串的長度。 後面再判斷字串是否一個負數, 當所有引數都驗證過了以後再做正事——將字串轉換成一個數字。

我現在得到的一個重要的經驗就是:

當你寫一個方法需要對傳入的引數進行處理或者計算的時候,你必須要嚴格驗證傳入引數的正確性,如果不符合,就應當給出提示!

上面提到的那篇文章裡說到:

這就是防禦性程式設計的最基本規則:保護程式免遭非法輸入資料的破壞。

這些都是我以前程式設計不會考慮的事情啊!

如果你的程式碼沒有防禦性措施,那麼你一定會遇到各種坑的~只是時候未到~

但也不是說所有的程式都應該這麼寫。如果你在寫一個 private 方法只供類裡面使用,那麼我覺得就不必寫這種防禦性程式碼了。當然沒有絕對的事情,如果一個 public 方法接受外部傳入的引數,這個引數又傳入這個 private 方法,那麼你在使用這個 private 方法時候就需要先驗證引數的合法性,然後再呼叫這個 private 方法。

當你在寫一個 public 方法可以接受來自任何地方的引數時,就必須要驗證引數的合法性了!

那麼為什麼要防禦性程式設計

我覺得最終的目的就是為了讓你寫的程式碼正確執行,當面對各種各樣的引數時,同時要向外部提供引數錯誤的原因,可以快速找到 bug。

在 Android 開發裡面,主執行緒(UI 執行緒)中的一個微小的問題都會導致程式的崩潰,可能是一不小心一個 View 物件傳入某個方法的時候是一個null,也可能一個方法的返回值是null等等,各種坑會在隱藏的地方等著你來踩哦~

要知道當一個物件為null的時候(你肯定不知道它為null),然後呼叫它的方法時,就會發生程式崩潰,這是應該是程式崩潰最常見的原因之一了~

比如在我最開始寫的那個 atoi 程式裡,如果別人用我的程式不小心傳入一個null,那麼我的程式就崩潰了~

本文轉載自: bxbxbai.github.io

相關閱讀
評論(0)

相關文章