防御性編程以及我的一些感想

jopen 10年前發布 | 6K 次閱讀 編程

  By bxbxbai

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

  在公司,我們碰到的很大一部分問題都是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);

}</pre>

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

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

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

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

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

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

  這就是防御性編程的最基本規則:保護程序免遭非法輸入數據的破壞。

</blockquote>

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

  如果你的代碼沒有防御性措施,那么你一定會遇到各種坑的~只是時候未到~

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

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

  那么為什么要防御性編程

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

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

  要知道當一個對象為null的時候(你肯定不知道它為null),然后調用它的方法時,就會發生程序崩潰,這是應該是程序崩潰最常見的原因之一了~

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

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!