談談代碼健壯性之極限值處理(防御性編程)


  我們知道,web開發的數據不斷在數據庫端、服務器端、客戶端進行傳遞。

  我們為了防止臟數據,我們需要對每個數據項的極限值進行特殊的處理;或者,換個角度來講,為了我們的代碼更加的健壯,我們不得不考慮所有與業務相關的極限值的處理。

  這里的”極限值處理“的定義比較寬泛,如

    1> 處理空值(如null/undefined/''等)
    2> 處理數據類型
    3> 處理數據范圍
    4> 其他與業務相關的特殊值或范圍的處理

  在這里,我僅僅說說有關前端方面的極限值的處理情況。主要包含兩點:

    1> 頁面顯示的極限值處理
    2> JavaScript函數參數的極限值處理

  下面我來分別介紹——

  

  一、頁面顯示的極限值處理

  舉個例子,freemarker顯示商品價格。或許一個馬虎的同學會這樣寫:

<!-- 什么極限值都沒有考慮 -->
<p class="price">
    <label>售價:</label>
    <span>${goods.price}</span>
</p>

  嗯,寫完,感覺還不錯,完成這個功能了。也沒有其他考慮代碼健壯性的意識,直到測試同學一次又一次的提示他糾正極限值的處理情況,於是乎,來來回回的更改。測試同學的提交bug流程、與程序同學的bug溝通過程,程序同學的代碼邏輯重新閱讀過程、修改過程,測試同學的回歸測試過程。

  真是浪費了不少時間去處理這個極限值。然而,處理的可能不夠徹底。及其可能它的處理完善過程如下——

<!-- 考慮了字段price的空值(默認值) -->
<p class="price">
    <label>售價:</label>
    <span>${goods.price!default("--")}</span>
</p>
<!-- 1.考慮了字段price的空值(默認值) -->
<!-- 2.考慮了對象goods的空值 -->
<p class="price">
    <label>售價:</label>
    <span>${goods?if_exists.price!default("--")}</span>
</p>
<!-- 1.考慮了字段price的空值(默認值) -->
<!-- 2.考慮了對象goods的空值 -->
<!-- 3.考慮了字段price的特殊(極限)值 -->
<p class="price">
    <label>售價:</label>
    <#if goods?if_exists.price?exists && goods?if_exists.price?int &gt; 0>
        <span>${goods.price}</span>
    <#else>
        <span class="no-data">--</span>
    </#if>
</p>

  對以上最后一個處理的比較全面的極限值的例子的解釋:首先判斷goods這個key是否存在(goods?if_exists),如果存在,再去判斷goods對象下的price字段是否存在(goods?if_exists.price?exists),緊接着判斷這里的price字段的是否大於0,如果大於0,則顯示該價格。不符合以上任何條件者,顯示“--”。

  一次又一次的更替,相信大家都不希望把時間浪費在一次又一次無謂的重復性的完善修改工作。根據業務需求,一次性考慮好極限情況,讓我們的代碼達到最佳健壯性,便會免去那些無謂的重復性工作。從用戶瀏覽的角度來講,能夠避免錯誤異常代碼的顯示,給予更加好的提示。

  總結一下頁面顯示的極限值處理——

    1> 考慮好每一個字段(包括基本數據類型、對象及對象的屬性)的極限值
    2> 這里的極限值處理可能包括:空值判斷(是否賦予默認值)、數據類型判斷、數據范圍判斷等
    3> 考慮好每一個極限值的顯示方式。依舊拿上面的例子來說,可能會有這樣的需求,如果price>1000,需要以“1,000”的方式進行顯示,當然這里就不涉及freemarker的控制顯示了,最好在程序后端進行處理。

  

  二、JavaScript函數參數的極限值處理

  這個話題也是一個大家常常忽視的問題,同樣也會引來測試同學與程序同學來來回回重復性工作的處理流程。我們細細的看一下這個話題。

  而今,數據參數的提交大多基於JavaScript來觸發,就連普普通通的表單提交,還要經過JavaScript的一輪校驗處理,校驗通過了,由JavaScript處理提交。

  既然如此,我們便需要考慮好JavaScript參數傳遞的極限值處理問題。先拿jQuery框架中的一個方法舉例吧——

jQuery.fn = jQuery.prototype = {
    
    get: function( num ) {
        
        //判斷是否為null,當然這里的undefined == null也是返回true
        return num == null ?
            
            // 如果為null,則返回所有符合的對應dom的數組對象
            // Return a 'clean' array
            this.toArray() :
            
            // 如果為非null,則再次判斷其他特殊情況
            // Return just the object
            ( num < 0 ? this[ this.length + num ] : this[ num ] );
    }
    
};

  嗯,極限值處理的很不錯,當num為負值時,考慮了使用逆序手法取出對應dom。

  我們拿幾個函數調用來測試一下它的健壯性——

<!-- 測試使用的html代碼 -->
<div class="test">1</div>
<div class="test">2</div>
<div class="test">3</div>
<div class="test">4</div>

<script src="lib/jquery-1.9.1.js"></script>

<script>
    
    //沒有參數 - 羅列滿足選擇條件的所有符合的dom對象並組成數組
    console.log($('.test').get());    //[div.test, div.test, div.test, div.test]

    //傳遞參數 - 數字1 - 選取滿足條件的第二個dom對象
    console.log($('.test').get(1));    //<div class="test">2</div>

    //傳遞參數 - 數字5 - 超出dom數組對象最大長度4,取值為undefined - 源於數組本身特性
    console.log($('.test').get(5));    //undefined
    
    //傳遞參數 - 字符串'1' - 選取滿足條件的第二個dom對象
    console.log($('.test').get('1'));    //<div class="test">2</div>
    
    //傳遞參數 - 非數字字符串'xxx' - 為undefined - 源於數組本身特性
    console.log($('.test').get('xxx'));    //undefined
    
</script>

  以上測試均沒有報錯,似乎已經滿足了我們的功能需要。

  但是,我們讀它的源碼,似乎依舊可以提高代碼的健壯性。如——

    1> 判斷參數num是否為數字
    2> 判斷參數num的絕對值是否小於數組長度

  對於該段源碼的健壯性改造如下——

jQuery.prototype.get = function ( num ) {
    
    //判斷是否為null
    if ( num == null ) {
        return this.toArray();
    }
    
    //判斷是否為數字
    if ( typeof num === 'number' ) {
        
        //判斷參數絕對值是否小於數組長度
        if ( Math.abs(num) < this.length ) {
            
            if ( num > 0 ) {    //為正數
                return this[ num ];
            } else {    //非正數
                return this[ this.length + num ];
            }
            
        }
        
    }
    
};

  這樣,不僅增強了代碼的健壯性,更加增強了其功能性代碼的可讀性。

  再舉一個例子吧——

 1 (function ($) {
 2     
 3     var events = {
 4         show: function (opt) {
 5             
 6             //這里對opt判空,如果為空,賦予一個默認值空對象{}
 7             opt = opt || {};
 8             
 9             //接下訪問opt對象里的屬性,就無須判斷opt是否為空了
10             if (opt.xxx) {    //替換 opt && opt.xxx
11                 //todo something
12             }
13             
14             if (opt.yyy) {    //替換 opt && opt.yyy
15                 //todo something
16             }
17             
18         },
19         close: function (opt) {
20             //...
21         },
22         lock: function (opt) {
23             //...
24         },
25         top: function (opt) {
26             //...
27         }
28     };
29     
30     $('.m-dialog').die('m-dialog').live('m-dialog',function(events){
31         
32         return function(opt){
33             
34             //一層一層做參數校驗
35             opt
36             && opt.event
37             && opt.event in events
38             && typeof events[opt.event] === 'function'
39             && events[opt.event].apply(this, arguments);
40             
41             return false;
42         }
43         
44     }(events));
45     
46 })(jQuery);

  以上兩個例子,更多的是涉及到的基礎判空的處理情況,並沒有涉到相關的業務方面的內容。可能這個部分是比較通用的,也是要常常需要去做處理的。當涉及到業務時,需要根據根據業務再去做對應極限值處理。

  在JavaScript的極限值處理過程中,絕大部分的處理均源於對函數參數的極限值處理,因此,我們不單單要關注每一個函數的功能單一性,同事我們需要培養對每一個函數參數做極限值處理的敏感思維,這樣,回頭我們才會覺得,我們的代碼是健壯的、是經得起考驗的。更加重要的是,免去了測試同學與程序同學來來回回的bug提交與修復的重復性工作,提高工作效率。

  

  三、總結

  這里,我僅僅對前端的兩個方面做了極限值處理的介紹,如下——

    1> 頁面顯示的極限值處理
    2> JavaScript函數參數的極限值處理

  當然,還有一個比較明顯的部分便是JavaScript控制傳遞參數到后端的極限值處理情況,也就是參數校驗了。有關這部分,就請看我的談談代碼健壯性之前端校驗這篇文章吧。我想這部分更多的極限值處理,便是賦予其傳參默認值了。

  

  四、擴展

  這里僅僅介紹了前端方面的極限值處理,不妨聯想一下后端的極限值處理,相信同樣如是。提高代碼健壯性,這些極限值便不得不多去思考。

  再擴展來說,無非是說對數據流的極限值的處理,我們需要對數據流的存儲、流向、更改的任何步驟都需要考慮每一個數據項的極限值,並采用合理方式處理之,方能提高代碼健壯性。

 

  歡迎各位博友批評指教,對處理極值有什么好的點子,不妨提供上來,一起討論、一起學習。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM