JavaScript設(shè)計模式——策略模式

策略模式是JavaScript設(shè)計模式中行為型的設(shè)計模式喳资;
定義:

定義一系列算法掉伏,并將這些算法各自封裝成策略類(方法)巩剖,然后將不變的部分和變化的部分分離開來适滓,并且這些算法可以相互替換

白話解釋:
   實際上所謂的策略模式就是值根據(jù)不同的策略來執(zhí)行不同的方法敦迄,是不是很類似與if-else分支判斷;但是策略模式是用來解決多重條件判斷語句的;

使用場景:
  需求:
  年終將至罚屋,某公司決定提前發(fā)年終獎苦囱,但是年終獎的計算是有一定的規(guī)則的,年終獎的多少跟績效考核密切相關(guān)脾猛;所以某公司的年終獎方案是這樣的:
    績效考核為S的員工撕彤,年終獎是個人月工資的4倍;
    績效考核為A的員工猛拴,年終獎是個人月工資的3倍羹铅;
    績效考核為B的員工,年終獎是個人月工資的2倍愉昆;
    看到這里讓你開始編寫程序职员,一般大部分的代碼是這樣的:


    function calculateBonus(level,salary){
        if(level === 'S'){
            return salary*4;
        }
        
        if(level === 'A'){
            return salary*3
        }
 
        if(level === 'B'){
            return salary*2
        }
    }
 
    console.log(calculateBonus("S",14000));  //56000
    console.log(calculateBonus("A",10000)); //30000
    console.log(calculateBonus("B",5000));  //10000

上面的代碼用來解決當(dāng)前需求固然沒有問題,但是在程序設(shè)計的角度來說跛溉,上面的代碼是還有可以優(yōu)化的點(diǎn)的焊切;因為該方法相對來說比較龐大,有很多的分支判斷芳室,缺乏彈性专肪;如果年終獎方案改了,需要增加一個C方案呢堪侯?那是不是又得去方法里面加分支判斷呢嚎尤?這就違反了開放封閉原則;

優(yōu)化:


    var strategies  = {
        "S":function(salary){
            return salary*4
        },
        "A":function(salary){
            return salary*3;
        },
        "B":function(salary){
            return salary*2
        }
    }
 
    var calculateBonus =function(level,salary){
        return strategies[level](salary);
    } 
    console.log(calculateBonus("S",14000));  //56000
    console.log(calculateBonus("A",10000));  //30000
    console.log(calculateBonus("B",5000));   //10000

通過優(yōu)化上述代碼之后伍宦,上面就是用策略模式來進(jìn)行改造代碼的诺苹,我們可以看到我們定義了一個策略對象,然后calculateBonus根據(jù)用戶傳入的等級和工資即可算出年終獎的金額雹拄,經(jīng)過改造之后,代碼的結(jié)構(gòu)變得更加簡潔掌呜;

在web開發(fā)中滓玖,登錄頁的注冊、登錄等功能都是需要進(jìn)行表單提交的质蕉;然而在提交的過程中肯定要進(jìn)行校驗和篩選势篡,不符合校驗規(guī)則的將不能直接提交;在沒有學(xué)習(xí)設(shè)計模式之前我們的校驗可能也是跟上面一樣都是多重if分支判斷模暗,然后我們現(xiàn)在用策略模式來實現(xiàn)一個表單校驗:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
        <form action="http:// xxx.com/register" id="registerForm" method="post">
            請輸入用戶名:<input type="text" name="userName"/ >
            請輸入密碼:<input type="text" name="password"/ >
            請輸入手機(jī)號碼:<input type="text" name="phoneNumber"/ >
            <button>提交</button>
        </form>
</body>
<script>
        // 定義策略類算法校驗規(guī)則
        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;
            }
        }
    };
    //Validator 類  
    var Validator = function(){
        // 保存校驗規(guī)則
        this.cache = [];
    };
    //添加校驗規(guī)則的方法
    Validator.prototype.add = function( dom, rules ){
        var self = this;
        for ( var i = 0, rule; rule = rules[ i++ ]; ){
            (function( rule ){
                //將校驗規(guī)則對象中的strategy屬性的值進(jìn)行分割
                var strategyAry = rule.strategy.split( ':' );
                var errorMsg = rule.errorMsg;
                self.cache.push(function(){
                    //將校驗規(guī)則對象中的strategy屬性的第一個值返回回來裝進(jìn)strategy中
                    var strategy = strategyAry.shift();
                    //組成參數(shù)
                    strategyAry.unshift( dom.value );
                    //組裝參數(shù)
                    strategyAry.push( errorMsg );
                    //找到策略對象執(zhí)行方法裝進(jìn)cache變量中
                    return strategies[ strategy ].apply( dom, strategyAry );
                });
                console.log(strategyAry);
            })( rule )
        }
    };
    //開始校驗方法
    Validator.prototype.start = function(){
        for ( var i = 0, validatorFunc; validatorFunc = this.cache[ i++ ]; ){
             //循環(huán)cache執(zhí)行方法校驗
            var errorMsg = validatorFunc();
            //如果執(zhí)行策略對象方法中返回了errorMsg禁悠,就說明方法已經(jīng)報錯(沒有通過校驗規(guī)則)
            if ( errorMsg ){
                return errorMsg;
            }
        }
    };
 
    //調(diào)用校驗
    var registerForm = document.getElementById( 'registerForm' );
    //定義方法可以自定義添加校驗規(guī)則
    var validataFunc = function(){
        //實例化對象
        var validator = new Validator();
        //自定義添加校驗規(guī)則
        validator.add( registerForm.userName, [{
            strategy: 'isNonEmpty',
            errorMsg: '用戶名不能為空'
        }, {
            strategy: 'minLength:6',
            errorMsg: '用戶名長度不能小于10 位'
        }]);
        validator.add( registerForm.password, [{
            strategy: 'minLength:6',
            errorMsg: '密碼長度不能小于6 位'
        }]);
        //調(diào)用方法循環(huán)執(zhí)行校驗
        var errorMsg = validator.start();
        return errorMsg;
    }
    //點(diǎn)擊提交按鈕(提交事件)
    registerForm.onsubmit = function(){
        //執(zhí)行上面自定義的校驗方法
        var errorMsg = validataFunc();
        //如果errorMsg存在,即代表校驗沒有通過
        if ( errorMsg ){
            alert ( errorMsg );
            return false;
        }
 
    };
</script>
</html>

我們可以通過策略模式來解決表單校驗大規(guī)模重復(fù)if-else判斷等問題兑宇,上面的代碼注釋我已經(jīng)給的很詳細(xì)了碍侦,學(xué)習(xí)設(shè)計模式一定要去細(xì)品代碼,學(xué)習(xí)思路;反正策略模式的一個主要思路就是通過定義一系列的算法瓷产,然后傳入?yún)?shù)站玄,根據(jù)不同的參數(shù)來執(zhí)行不同的算法規(guī)則;

優(yōu)缺點(diǎn):
 優(yōu)點(diǎn):   
    1濒旦、利用組合株旷、委托和多態(tài)技術(shù)和思想,可以避免多重條件選擇語句尔邓;
    2晾剖、將算法封裝在獨(dú)立的策略類里,使得易于切換梯嗽,易于理解齿尽,易于擴(kuò)展;
    3慷荔、策略模式可以復(fù)用在系統(tǒng)的其他地方雕什,從而避免重復(fù)的復(fù)制粘貼工作;   
 缺點(diǎn):   
    1显晶、程序中會增加許多策略類或者策略對象贷岸;
    2、使用策略類必須要對所有的策略類算法了解清楚磷雇,否則不知道怎么選擇偿警。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市唯笙,隨后出現(xiàn)的幾起案子螟蒸,更是在濱河造成了極大的恐慌,老刑警劉巖崩掘,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件七嫌,死亡現(xiàn)場離奇詭異,居然都是意外死亡苞慢,警方通過查閱死者的電腦和手機(jī)诵原,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來挽放,“玉大人绍赛,你說我怎么就攤上這事〖瑁” “怎么了吗蚌?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長纯出。 經(jīng)常有香客問我蚯妇,道長敷燎,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任侮措,我火速辦了婚禮懈叹,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘分扎。我一直安慰自己澄成,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布畏吓。 她就那樣靜靜地躺著墨状,像睡著了一般。 火紅的嫁衣襯著肌膚如雪菲饼。 梳的紋絲不亂的頭發(fā)上肾砂,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機(jī)與錄音宏悦,去河邊找鬼镐确。 笑死,一個胖子當(dāng)著我的面吹牛饼煞,可吹牛的內(nèi)容都是我干的源葫。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼砖瞧,長吁一口氣:“原來是場噩夢啊……” “哼息堂!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起块促,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤荣堰,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后竭翠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體振坚,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年斋扰,在試婚紗的時候發(fā)現(xiàn)自己被綠了屡拨。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡褥实,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出裂允,到底是詐尸還是另有隱情损离,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布绝编,位于F島的核電站僻澎,受9級特大地震影響貌踏,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜窟勃,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一祖乳、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧秉氧,春花似錦眷昆、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至攘滩,卻和暖如春帅刊,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背漂问。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工赖瞒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蚤假。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓栏饮,卻偏偏與公主長得像,于是被迫代替她去往敵國和親勤哗。 傳聞我的和親對象是個殘疾皇子抡爹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容