JavaScript策略模式

策略模式在Angular中用到了,這里給大家分享一篇文章,看過之后便于理解angular

1摊欠、策略模式的定義:定義一系列算法丢烘,把他們一個(gè)個(gè)封裝起來,并且使他們可以相互替換
優(yōu)點(diǎn):(1)策略模式利用組合些椒,委托等技術(shù)和思想播瞳,有效避免多if條件語句
(2)策略模式提供了,開放-封閉原則免糕,使代碼更容易理解和擴(kuò)展赢乓。
(3)策略模式的代碼可以復(fù)用
2、一:使用策略模式計(jì)算獎(jiǎng)金说墨;
下面的demo是我在書上看到的骏全,但是沒有關(guān)系,我們只是來理解下策略模式的使用而已尼斧,我們可以使用策略模式來計(jì)算獎(jiǎng)金問題姜贡;比如公司的年終獎(jiǎng)是根據(jù)員工的工資和績效來考核的,績效為A的人棺棵,年終獎(jiǎng)為工資的4倍楼咳,績效為B的人,年終獎(jiǎng)為工資的3倍烛恤,績效為C的人巢钓,年終獎(jiǎng)為工資的2倍内地;現(xiàn)在我們使用一般的編碼方式會(huì)如下這樣編寫代碼:var

calculateBous=function(salary,level){
     if(level==="A"){
          return salary*4;
     }
     if(level==="B"){
          return salary*3;
     }
     if(level==="C"){
          return salary*2;
     }
};
console.log(calculateBounds(4000,'A'));
console.log(calculateBounds(2500,'B'));

缺點(diǎn):(1)calculateBouns函數(shù)包含了很多if-else語句
(2)calculateBouns函數(shù)缺乏彈性,不能擴(kuò)展
(3)算法復(fù)用性差,算法不能通用

  1. 使用組合函數(shù)重構(gòu)代碼組合函數(shù)是把各種算法封裝到一個(gè)個(gè)的小函數(shù)里面破婆,比如等級(jí)A的話,封裝一個(gè)小函數(shù)变汪,等級(jí)為B的話伤为,也封裝一個(gè)小函數(shù),以此類推杀餐;如下代碼:
var performanceA=function(salary){
     return salary*4;
};
var performanceB=function(salary){
     return salary*3;
};
var performanceC=function(salary){
     return salary*2;
};
var calculateBouns=function(salary,level){
     if(levle==='A'){
          return performanc(salary);
     }
     if(levle==='B'){
          return performanc(salary);
     }
    if(levle==='C'){
          return performanc(salary);
     }
};
console.log(calculateBouns('A',4500));

代碼看起來有點(diǎn)改善干发,但是還是有如下缺點(diǎn):calculateBouns 函數(shù)有可能會(huì)越來越大,比如增加D等級(jí)的時(shí)候史翘,而且缺乏彈性枉长。
3、使用策略模式重構(gòu)代碼
策略模式指的是定義一系列算法琼讽,把它們一個(gè)個(gè)封裝起來必峰,將不變的部分和變化的部分隔開,實(shí)際就是將算法的使用和實(shí)現(xiàn)分離出來钻蹬;算法的使用方式不變的自点,都是根據(jù)根據(jù)某個(gè)算法計(jì)算后的獎(jiǎng)金數(shù),而算法的實(shí)現(xiàn)時(shí)根據(jù)績效對(duì)應(yīng)不同的績效規(guī)則脉让;
一個(gè)基于策略模式的程序至少由兩部分組成桂敛,第一部分是策略類功炮,策略類封裝了具體的算法,并負(fù)責(zé)具體的計(jì)算過程术唬。第二部分是環(huán)境類context薪伏,該context接受客戶端的請(qǐng)求,隨后把請(qǐng)求委托給某個(gè)策略類粗仓。
使用傳統(tǒng)面向?qū)ο髞韺?shí)現(xiàn):

var performanceA=function(){};
performanceA.prototype.calculate=function(salary){
     return salary*4;
};
var performanceB=function(){};
performanceB.prototype.calculate=function(salary){
     return salary*3;
};
var performanceC=function(){};
performanceC.prototype.calculate=function(salary){
     return salary*3;
};
//獎(jiǎng)金類
var Bouns=funtion(){
     this.salary=null; //原始工資
     this.levelObj=null; //績效等級(jí)對(duì)應(yīng)的策略
};
Bounds.prototype.setSalry=function(salary){
     this.salry=salary;  //保存原始工資
};
Bounds.prototype.setLvelObj=function(salary){
     this.setLevleObj=levelObj;  //設(shè)置員工的績效等級(jí)
};
//取得獎(jiǎng)金
Bouns.prototype.getBouns=function(){
     return this.levelObj.calculate(this.salary);
};
var bouns= new Bouns();
bouns.setSalary(10000);
bouns.setLevelObj(new performanceA); //設(shè)置策略對(duì)象
console.log(bouns.getBouns()); //40000
//對(duì)于prototype的方法或者屬性嫁怀,我們可以動(dòng)態(tài)的增加,而由其創(chuàng)建的對(duì)象自動(dòng)會(huì)繼承相關(guān)的方法和屬性

bouns.setLevelObj(new performanceB);
console.log(bouns.getBouns());
  1. Javascript版本的策略模式代碼如下:
var obj={
     "A":function(salary){
       return salary*4;   
     },
     "B":function(salary){
          return salary*3;
     },
     "C":funtion (salary){
          return salary*2;
     }
};
var calculateBouns=function(level,salary){
     return obj[level][salary];
};
console.log(calculateBouns('A',1000));

可以看到代碼更加簡單明了借浊;策略模式指的是定義一系列的算法塘淑,并且把它們封裝起來,但是策略模式不僅僅只封裝算法蚂斤,我們還可以對(duì)用來封裝一系列的業(yè)務(wù)規(guī)則存捺,只要這些業(yè)務(wù)規(guī)則目標(biāo)一致,我們就可以使用策略模式來封裝它們曙蒸;
5捌治、
比如我們經(jīng)常來進(jìn)行表單驗(yàn)證,比如注冊(cè)登錄對(duì)話框纽窟,我們登錄之前要進(jìn)行驗(yàn)證操作:比如有以下幾條邏輯:
1.用戶名不能為空2.密碼長度不能小于6位肖油。
3.手機(jī)號(hào)碼必須符合格式。比如HTML代碼如下:

<form action = "http://www.baidu.com" id="registerForm" method = "post">
        <p>
            <label>請(qǐng)輸入用戶名:</label>
            <input type="text" name="userName"/>
        </p> 
       <p>
            <label>請(qǐng)輸入密碼:</label>
            <input type="text" name="password"/>
        </p>
        <p>
            <label>請(qǐng)輸入手機(jī)號(hào)碼:</label>
            <input type="text" name="phoneNumber"/>
        </p>
</form>

我們正常的表單編寫如下:

var registerForm=document.getElementById("registerForm");
registerForm.onsubmit=function(){
     if(registerForm.usename.value=""){
          alert('用戶名不能為空');
          return臂港;
     }
     if(registerForm.password.value.length<6){
          alert("密碼長度不能小于6位");
          return森枪;
     }
     if(!/(^1[3|5|8][0-9]{9}$)/.test(registerForm.phoneNumber.value)) {
        alert("手機(jī)號(hào)碼格式不正確"); 
       return;
};

但是這樣編寫代碼有如下缺點(diǎn):1.registerForm.onsubmit 函數(shù)比較大,代碼中包含了很多if語句审孽;2.registerForm.onsubmit 函數(shù)缺乏彈性县袱,如果增加了一種新的效驗(yàn)規(guī)則,或者想把密碼的長度效驗(yàn)從6改成8瓷胧,我們必須改registerForm.onsubmit 函數(shù)內(nèi)部的代碼显拳。違反了開放-封閉原則棚愤。3.算法的復(fù)用性差搓萧,如果在程序中增加了另外一個(gè)表單,這個(gè)表單也需要進(jìn)行一些類似的效驗(yàn)宛畦,那么我們可能又需要復(fù)制代碼了瘸洛;下面我們可以使用策略模式來重構(gòu)表單效驗(yàn);第一步我們先來封裝策略對(duì)象次和;如下代碼:

var strategy = { 
   isNotEmpty: function(value,errorMsg) {
        if(value === '') { 
           return errorMsg;
        } 
   },    // 限制最小長度
    minLength: function(value,length,errorMsg) {
        if(value.length < length) { 
           return errorMsg;
        } 
   },    // 手機(jī)號(hào)碼格式
    mobileFormat: function(value,errorMsg) { 
       if(!/(^1[3|5|8][0-9]{9}$)/.test(value)) { 
           return errorMsg; 
       }
    }};

接下來我們準(zhǔn)備實(shí)現(xiàn)Validator類反肋,Validator類在這里作為Context,負(fù)責(zé)接收用戶的請(qǐng)求并委托給strategy 對(duì)象踏施,如下代碼:

var Validator = function(){
        this.cache = [];  // 保存效驗(yàn)規(guī)則
};
Validator.prototype.add = function(dom,rule,errorMsg) {
    var str = rule.split(":");
    this.cache.push(function(){
        // str 返回的是 minLength:6
        var strategy = str.shift();
        str.unshift(dom.value); // 把input的value添加進(jìn)參數(shù)列表
        str.push(errorMsg);  // 把errorMsg添加進(jìn)參數(shù)列表
        return strategys[strategy].apply(dom,str);
    });
};
Validator.prototype.start = function(){
    for(var i = 0, validatorFunc; validatorFunc = this.cache[i++]; ) {
        var msg = validatorFunc(); // 開始效驗(yàn) 并取得效驗(yàn)后的返回信息
        if(msg) {
            return msg;
        }
    }
};

Validator類在這里作為Context石蔗,負(fù)責(zé)接收用戶的請(qǐng)求并委托給strategys對(duì)象罕邀。上面的代碼中,我們先創(chuàng)建一個(gè)Validator對(duì)象养距,然后通過validator.add方法往validator對(duì)象中添加一些效驗(yàn)規(guī)則诉探,validator.add方法接收3個(gè)參數(shù),如下代碼:
validator.add(registerForm.password,’minLength:6′,’密碼長度不能小于6位’);
registerForm.password 為效驗(yàn)的input輸入框dom節(jié)點(diǎn)棍厌;
minLength:6: 是以一個(gè)冒號(hào)隔開的字符串肾胯,冒號(hào)前面的minLength代表客戶挑選的strategys對(duì)象,冒號(hào)后面的數(shù)字6表示在效驗(yàn)過程中所必須驗(yàn)證的參數(shù)耘纱,minLength:6的意思是效驗(yàn) registerForm.password 這個(gè)文本輸入框的value最小長度為6位敬肚;如果字符串中不包含冒號(hào),說明效驗(yàn)過程中不需要額外的效驗(yàn)信息束析;
第三個(gè)參數(shù)是當(dāng)效驗(yàn)未通過時(shí)返回的錯(cuò)誤信息艳馒;
當(dāng)我們往validator對(duì)象里添加完一系列的效驗(yàn)規(guī)則之后,會(huì)調(diào)用validator.start()方法來啟動(dòng)效驗(yàn)畸陡。如果validator.start()返回了一個(gè)errorMsg字符串作為返回值鹰溜,說明該次效驗(yàn)沒有通過,此時(shí)需要registerForm.onsubmit方法返回false來阻止表單提交丁恭。下面我們來看看初始化代碼如下:

var validateFunc = function(){
    var validator = new Validator(); // 創(chuàng)建一個(gè)Validator對(duì)象
    /* 添加一些效驗(yàn)規(guī)則 */
    validator.add(registerForm.userName,'isNotEmpty','用戶名不能為空');
    validator.add(registerForm.password,'minLength:6','密碼長度不能小于6位');
    validator.add(registerForm.userName,'mobileFormat','手機(jī)號(hào)碼格式不正確');
    var errorMsg = validator.start(); // 獲得效驗(yàn)結(jié)果
    return errorMsg; // 返回效驗(yàn)結(jié)果
};
var registerForm = document.getElementById("registerForm");
registerForm.onsubmit = function(){
    var errorMsg = validateFunc();
    if(errorMsg){
        alert(errorMsg);
        return false;
    }
}

下面是所有代碼:

var strategys = {
    isNotEmpty: function(value,errorMsg) {
        if(value === '') {
            return errorMsg;
        }
    },
    // 限制最小長度
    minLength: function(value,length,errorMsg) {
        if(value.length < length) {
            return errorMsg;
        }
    },
    // 手機(jī)號(hào)碼格式
    mobileFormat: function(value,errorMsg) {
        if(!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
            return errorMsg;
        }
    }
};
var Validator = function(){
    this.cache = [];  // 保存效驗(yàn)規(guī)則
};
Validator.prototype.add = function(dom,rule,errorMsg) {
    var str = rule.split(":");
    this.cache.push(function(){
        // str 返回的是 minLength:6
        var strategy = str.shift();
        str.unshift(dom.value); // 把input的value添加進(jìn)參數(shù)列表
        str.push(errorMsg);  // 把errorMsg添加進(jìn)參數(shù)列表
        return strategys[strategy].apply(dom,str);
    });
};
Validator.prototype.start = function(){
    for(var i = 0, validatorFunc; validatorFunc = this.cache[i++]; ) {
        var msg = validatorFunc(); // 開始效驗(yàn) 并取得效驗(yàn)后的返回信息
        if(msg) {
            return msg;
        }
    }
};

var validateFunc = function(){
    var validator = new Validator(); // 創(chuàng)建一個(gè)Validator對(duì)象
    /* 添加一些效驗(yàn)規(guī)則 */
    validator.add(registerForm.userName,'isNotEmpty','用戶名不能為空');
    validator.add(registerForm.password,'minLength:6','密碼長度不能小于6位');
    validator.add(registerForm.userName,'mobileFormat','手機(jī)號(hào)碼格式不正確');
    var errorMsg = validator.start(); // 獲得效驗(yàn)結(jié)果
    return errorMsg; // 返回效驗(yàn)結(jié)果
};
var registerForm = document.getElementById("registerForm");
registerForm.onsubmit = function(){
    var errorMsg = validateFunc();
    if(errorMsg){
        alert(errorMsg);
        return false;
    }
};

如上使用策略模式來編寫表單驗(yàn)證代碼可以看到好處了曹动,我們通過add配置的方式就完成了一個(gè)表單的效驗(yàn);這樣的話牲览,那么代碼可以當(dāng)做一個(gè)組件來使用墓陈,并且可以隨時(shí)調(diào)用,在修改表單驗(yàn)證規(guī)則的時(shí)候第献,也非常方便贡必,通過傳遞參數(shù)即可調(diào)用;
給某個(gè)文本輸入框添加多種效驗(yàn)規(guī)則
上面的代碼我們可以看到庸毫,我們只是給輸入框只能對(duì)應(yīng)一種效驗(yàn)規(guī)則仔拟,比如上面的我們只能效驗(yàn)輸入框是否為空,
validator.add(registerForm.userName,’isNotEmpty’,'用戶名不能為空’);但是如果我們既要效驗(yàn)輸入框是否為空飒赃,還要效驗(yàn)輸入框的長度不要小于10位的話利花,那么我們期望需要像如下傳遞參數(shù):
validator.add(registerForm.userName,[{strategy:’isNotEmpty’,errorMsg:’用戶名不能為空’},{strategy: 'minLength:6',errorMsg:'用戶名長度不能小于6位'}])
我們可以編寫代碼如下:

// 策略對(duì)象
var strategys = {
    isNotEmpty: function(value,errorMsg) {
        if(value === '') {
            return errorMsg;
        }
    },
    // 限制最小長度
    minLength: function(value,length,errorMsg) {
        if(value.length < length) {
            return errorMsg;
        }
    },
    // 手機(jī)號(hào)碼格式
    mobileFormat: function(value,errorMsg) {
        if(!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
            return errorMsg;
        }
    }
};
var Validator = function(){
    this.cache = [];  // 保存效驗(yàn)規(guī)則
};
Validator.prototype.add = function(dom,rules) {
    var self = this;
    for(var i = 0, rule; rule = rules[i++]; ){
        (function(rule){
            var strategyAry = rule.strategy.split(":");
            var errorMsg = rule.errorMsg;
            self.cache.push(function(){
                var strategy = strategyAry.shift();
                strategyAry.unshift(dom.value);
                strategyAry.push(errorMsg);
                return strategys[strategy].apply(dom,strategyAry);
            });
        })(rule);
    }
};
Validator.prototype.start = function(){
    for(var i = 0, validatorFunc; validatorFunc = this.cache[i++]; ) {
    var msg = validatorFunc(); // 開始效驗(yàn) 并取得效驗(yàn)后的返回信息
    if(msg) {
        return msg;
    }
    }
};
// 代碼調(diào)用
var registerForm = document.getElementById("registerForm");
var validateFunc = function(){
    var validator = new Validator(); // 創(chuàng)建一個(gè)Validator對(duì)象
    /* 添加一些效驗(yàn)規(guī)則 */
    validator.add(registerForm.userName,[
        {strategy: 'isNotEmpty',errorMsg:'用戶名不能為空'},
        {strategy: 'minLength:6',errorMsg:'用戶名長度不能小于6位'}
    ]);
    validator.add(registerForm.password,[
        {strategy: 'minLength:6',errorMsg:'密碼長度不能小于6位'},
    ]);
    validator.add(registerForm.phoneNumber,[
        {strategy: 'mobileFormat',errorMsg:'手機(jī)號(hào)格式不正確'},
    ]);
    var errorMsg = validator.start(); // 獲得效驗(yàn)結(jié)果
    return errorMsg; // 返回效驗(yàn)結(jié)果
};
// 點(diǎn)擊確定提交
registerForm.onsubmit = function(){
    var errorMsg = validateFunc();
    if(errorMsg){
        alert(errorMsg);
        return false;
    }
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末载佳,一起剝皮案震驚了整個(gè)濱河市炒事,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蔫慧,老刑警劉巖挠乳,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡睡扬,警方通過查閱死者的電腦和手機(jī)盟蚣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來卖怜,“玉大人刁俭,你說我怎么就攤上這事∪驼牵” “怎么了牍戚?”我有些...
    開封第一講書人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長虑粥。 經(jīng)常有香客問我如孝,道長,這世上最難降的妖魔是什么娩贷? 我笑而不...
    開封第一講書人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任第晰,我火速辦了婚禮,結(jié)果婚禮上彬祖,老公的妹妹穿的比我還像新娘茁瘦。我一直安慰自己,他們只是感情好储笑,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開白布甜熔。 她就那樣靜靜地躺著,像睡著了一般突倍。 火紅的嫁衣襯著肌膚如雪腔稀。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,287評(píng)論 1 301
  • 那天羽历,我揣著相機(jī)與錄音焊虏,去河邊找鬼。 笑死秕磷,一個(gè)胖子當(dāng)著我的面吹牛诵闭,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播澎嚣,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼疏尿,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了币叹?” 一聲冷哼從身側(cè)響起润歉,我...
    開封第一講書人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤模狭,失蹤者是張志新(化名)和其女友劉穎颈抚,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡贩汉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年驱富,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片匹舞。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡褐鸥,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出赐稽,到底是詐尸還是另有隱情叫榕,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布姊舵,位于F島的核電站晰绎,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏括丁。R本人自食惡果不足惜荞下,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望史飞。 院中可真熱鬧尖昏,春花似錦、人聲如沸构资。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽吐绵。三九已至掸鹅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間拦赠,已是汗流浹背巍沙。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留荷鼠,地道東北人句携。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像允乐,于是被迫代替她去往敵國和親矮嫉。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354

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

  • 工廠模式類似于現(xiàn)實(shí)生活中的工廠可以產(chǎn)生大量相似的商品牍疏,去做同樣的事情蠢笋,實(shí)現(xiàn)同樣的效果;這時(shí)候需要使用工廠模式。簡單...
    舟漁行舟閱讀 7,752評(píng)論 2 17
  • 單例模式 適用場景:可能會(huì)在場景中使用到對(duì)象鳞陨,但只有一個(gè)實(shí)例昨寞,加載時(shí)并不主動(dòng)創(chuàng)建,需要時(shí)才創(chuàng)建 最常見的單例模式,...
    Obeing閱讀 2,065評(píng)論 1 10
  • 介紹 策略模式的定義是:定義一系列的算法援岩,把它們一個(gè)個(gè)封裝起來歼狼,并且使它們可以相互替換。 在程序設(shè)計(jì)中享怀,我們也常常...
    悟空你又瘦了閱讀 611評(píng)論 0 2
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法羽峰,類相關(guān)的語法,內(nèi)部類的語法添瓷,繼承相關(guān)的語法梅屉,異常的語法,線程的語...
    子非魚_t_閱讀 31,625評(píng)論 18 399
  • 不得不說鳞贷,武則天賦予了那個(gè)時(shí)代獨(dú)特的勇氣履植,使女人們紛紛不安于室,開始熱衷于男人們的權(quán)利游戲悄晃,纖纖素手?jǐn)嚺龀蔑L(fēng)云...
    景小菡閱讀 987評(píng)論 13 24