javascript設計模式之二——策略模式

策略模式

策略模式:定義一些算法,把它們一個個封裝起來,使它們可以互相替換。此模式讓算法的變化不會影響到使用算法的客戶。
應用場景:在設計程序中,當我們實現某種功能的時候,他有很多種選擇,這些算法靈活多樣,這時候我們需要用到策略模式,將每個算法封裝起來,可以交替使用。實踐中,不僅可以封裝算法,也可以用來封裝幾乎任何類型的規則,是要在分析過程中需要在不同時間應用不同的業務規則,就可以考慮是要策略模式來處理各種變化。
策略模式的程序至少包含兩個部分:第一部分是一組策略類,策略類封裝了具體的算法,負責具體的計算過程;第二部分是環境類,環境類是接受用戶的請求,隨後把請求委託給某個策略類。
實現代碼:
實例場景:在web項目中,我們常常會遇到註冊、登錄、修改等功能的實現,這都離不開表單的提交,在提交表單時,我們需要驗證數據的合理性。如果用if-else語句來判斷,這些語句需要覆蓋所有的邏輯分支,同時,如果要增加新的條件,必須到這段代碼內部進行修改,違反了開放-封閉原則,並且代碼的複用性很差,這時,我們需要使用策略模式。
下面以表單提交爲例,來理解策略模式的思想。
在不用策略模式的情況下,我們可能會這樣處理:
代碼如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
</head>
<body>
        <form action="http:// xxx.com/register" id="registerForm" method="post">
    請輸入用戶名:<input type="text" name="userName"/>
    請輸入密碼:<input type="text" name="password"/>
    請輸入手機號碼:<input type="text" name="phoneNumber"/>
    <button>提交</button>
</form>
<script>
    var registerForm = document.getElementById( 'registerForm' );
    registerForm.onsubmit = function(){
        if ( registerForm.userName.value === '' ){
            alert ( '用戶名不能爲空' );
            return false;
        }
        if ( registerForm.password.value.length < 6 ){
            alert ( '密碼長度不能少於6位' );
            return false;
        }
        if ( !/(^1[3|5|8][0-9]{9}$)/.test( registerForm.phoneNumber.value ) ){
            alert ( '手機號碼格式不正確' );
            return false;
        }
    }
</script>
</body>
</html>

上述代碼雖然實現了最基本的功能,但是存在的問題如下:
(1)registerForm.onsubmit函數中包含大量的if-else語句,必須包含所有的校驗規則,這是函數的代碼量比較龐大;
(2)registerForm.onsubmit函數缺乏彈性,如果要增加新的規則或修改某些規則,必須對該函數進行修改,不利於代碼的維護,違反了開放-封閉規則;
(3)代碼的可複用性較差,當其他地方也需要使用該校驗規則時,必須複製該代碼,導致代碼混亂。
爲了避免以上問題,我們用策略模式進行設計,代碼如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
</head>
<body>

<form action="http:// xxx.com/register" id="registerForm" method="post">
    請輸入用戶名:<input type="text" name="userName"/>
    請輸入密碼:<input type="text" name="password"/>
    請輸入手機號碼:<input type="text" name="phoneNumber"/>
    <button>提交</button>
</form>
<script>
    //step1:將校驗邏輯封裝成策略對象,包含所有需要校驗的信息
    var strategies = {
        isNonEmpty: function (value, errorMsg) {//輸入不爲空
            if (value === '') {
                return errorMsg;
            }
        },

        minLength: function (value, length, errorMsg) {//測試輸入最小長度
            if (value.length < length) {
                return errorMsg;
            }
        },

        isMobile: function (value, errorMsg) { //手機號碼格式
            if (!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
                return errorMsg;
            }
        }

    };

    // step2: Validator類作爲環境類,接受用戶的請求,用戶的校驗信息,隨後把請求委託給strategies類
    var validataFunc = function () {
        var validator = new Validator();    // 創建一個validator對象
        /****************添加一些校驗規則****************/
        validator.add(registerForm.userName, 'isNonEmpty', '用戶名不能爲空');
        validator.add(registerForm.password, 'minLength:6', '密碼長度不能少於6位');
        validator.add(registerForm.phoneNumber, 'isMobile', '手機號碼格式不正確');
        var errorMsg = validator.start(); // 獲得校驗結果
        return errorMsg; // 返回校驗結果
    };
    var Validator = function () {
        this.cache = []; // 保存校驗規則
    };
    Validator.prototype.add = function (dom, rule, errorMsg) {
        var ary = rule.split(':'); // 把strategy和參數分開
        this.cache.push(function () { // 把校驗的步驟用空函數包裝起來,並且放入cache
            var strategy = ary.shift(); // 用戶挑選的strategy
            ary.unshift(dom.value); // 把input的value添加進參數列表
            ary.push(errorMsg); // 把errorMsg添加進參數列表
            return strategies[strategy].apply(dom, ary);
        });
    };
    Validator.prototype.start = function () {
        for (var i = 0, validatorFunc; validatorFunc = this.cache[i++];) {
            var msg = validatorFunc(); // 開始校驗,並取得校驗後的返回信息
            if (msg) { // 如果有確切的返回值,說明校驗沒有通過
                return msg;
            }
        }
    };

    var registerForm = document.getElementById('registerForm');
    registerForm.onsubmit = function () {
        var errorMsg = validataFunc(); // 如果errorMsg有確切的返回值,說明未通過校驗
        if (errorMsg) {
            alert(errorMsg);
            return false; // 阻止表單提交
        }
    };
</script>
</body>
</html>

使用策略模式後,我們不需要使用if-else語句,僅僅通過“配置”的方式就可以完成表單的提交;當規則要添加或者修改時,只需要修改或編寫少量代碼即可完成,擴展性好;並且,代碼有很好的複用性。
策略模式的優缺點:
優點:
1、策略模式利用組合、委託和多態等技術和思想,可以有效地避免多重條件選擇語句。
2、策略模式提供了對開放—封閉原則的完美支持,將算法封裝在獨立的strategy中,使得它們易於切換,易於理解,易於擴展。
3、策略模式中的算法也可以複用在系統的其他地方,從而避免許多重複的複製粘貼工作。
4、在策略模式中利用組合和委託來讓Context擁有執行算法的能力,這也是繼承的一種更輕便的替代方案。
缺點:
1、使用策略模式會在程序中增加許多策略類或者策略對象,但實際上這比把它們負責的邏輯堆砌在Context中要好。
2、要使用策略模式,必須瞭解所有的strategy,必須瞭解各個strategy之間的不
同點,這樣才能選擇一個合適的strategy。比如,我們要選擇一種合適的旅遊出行路
線,必須先了解選擇飛機、火車、自行車等方案的細節。此時strategy要向客戶暴露
它的所有實現,這是違反最少知識原則的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章