JavaScript策略模式

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

1、策略模式的定義:定義一系列算法,把他們一個個封裝起來,并且使他們可以相互替換
優點:(1)策略模式利用組合,委托等技術和思想,有效避免多if條件語句
(2)策略模式提供了,開放-封閉原則,使代碼更容易理解和擴展。
(3)策略模式的代碼可以復用
2、一:使用策略模式計算獎金;
下面的demo是我在書上看到的,但是沒有關系,我們只是來理解下策略模式的使用而已,我們可以使用策略模式來計算獎金問題;比如公司的年終獎是根據員工的工資和績效來考核的,績效為A的人,年終獎為工資的4倍,績效為B的人,年終獎為工資的3倍,績效為C的人,年終獎為工資的2倍;現在我們使用一般的編碼方式會如下這樣編寫代碼: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'));

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

  1. 使用組合函數重構代碼組合函數是把各種算法封裝到一個個的小函數里面,比如等級A的話,封裝一個小函數,等級為B的話,也封裝一個小函數,以此類推;如下代碼:
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));

代碼看起來有點改善,但是還是有如下缺點:calculateBouns 函數有可能會越來越大,比如增加D等級的時候,而且缺乏彈性。
3、使用策略模式重構代碼
策略模式指的是定義一系列算法,把它們一個個封裝起來,將不變的部分和變化的部分隔開,實際就是將算法的使用和實現分離出來;算法的使用方式不變的,都是根據根據某個算法計算后的獎金數,而算法的實現時根據績效對應不同的績效規則;
一個基于策略模式的程序至少由兩部分組成,第一部分是策略類,策略類封裝了具體的算法,并負責具體的計算過程。第二部分是環境類context,該context接受客戶端的請求,隨后把請求委托給某個策略類。
使用傳統面向對象來實現:

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;
};
//獎金類
var Bouns=funtion(){
     this.salary=null; //原始工資
     this.levelObj=null; //績效等級對應的策略
};
Bounds.prototype.setSalry=function(salary){
     this.salry=salary;  //保存原始工資
};
Bounds.prototype.setLvelObj=function(salary){
     this.setLevleObj=levelObj;  //設置員工的績效等級
};
//取得獎金
Bouns.prototype.getBouns=function(){
     return this.levelObj.calculate(this.salary);
};
var bouns= new Bouns();
bouns.setSalary(10000);
bouns.setLevelObj(new performanceA); //設置策略對象
console.log(bouns.getBouns()); //40000
//對于prototype的方法或者屬性,我們可以動態的增加,而由其創建的對象自動會繼承相關的方法和屬性

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));

可以看到代碼更加簡單明了;策略模式指的是定義一系列的算法,并且把它們封裝起來,但是策略模式不僅僅只封裝算法,我們還可以對用來封裝一系列的業務規則,只要這些業務規則目標一致,我們就可以使用策略模式來封裝它們;
5、
比如我們經常來進行表單驗證,比如注冊登錄對話框,我們登錄之前要進行驗證操作:比如有以下幾條邏輯:
1.用戶名不能為空2.密碼長度不能小于6位。
3.手機號碼必須符合格式。比如HTML代碼如下:

<form action = "http://www.baidu.com" id="registerForm" method = "post">
        <p>
            <label>請輸入用戶名:</label>
            <input type="text" name="userName"/>
        </p> 
       <p>
            <label>請輸入密碼:</label>
            <input type="text" name="password"/>
        </p>
        <p>
            <label>請輸入手機號碼:</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("手機號碼格式不正確"); 
       return;
};

但是這樣編寫代碼有如下缺點:1.registerForm.onsubmit 函數比較大,代碼中包含了很多if語句;2.registerForm.onsubmit 函數缺乏彈性,如果增加了一種新的效驗規則,或者想把密碼的長度效驗從6改成8,我們必須改registerForm.onsubmit 函數內部的代碼。違反了開放-封閉原則。3.算法的復用性差,如果在程序中增加了另外一個表單,這個表單也需要進行一些類似的效驗,那么我們可能又需要復制代碼了;下面我們可以使用策略模式來重構表單效驗;第一步我們先來封裝策略對象;如下代碼:

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

接下來我們準備實現Validator類,Validator類在這里作為Context,負責接收用戶的請求并委托給strategy 對象,如下代碼:

var Validator = function(){
        this.cache = [];  // 保存效驗規則
};
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添加進參數列表
        str.push(errorMsg);  // 把errorMsg添加進參數列表
        return strategys[strategy].apply(dom,str);
    });
};
Validator.prototype.start = function(){
    for(var i = 0, validatorFunc; validatorFunc = this.cache[i++]; ) {
        var msg = validatorFunc(); // 開始效驗 并取得效驗后的返回信息
        if(msg) {
            return msg;
        }
    }
};

Validator類在這里作為Context,負責接收用戶的請求并委托給strategys對象。上面的代碼中,我們先創建一個Validator對象,然后通過validator.add方法往validator對象中添加一些效驗規則,validator.add方法接收3個參數,如下代碼:
validator.add(registerForm.password,’minLength:6′,’密碼長度不能小于6位’);
registerForm.password 為效驗的input輸入框dom節點;
minLength:6: 是以一個冒號隔開的字符串,冒號前面的minLength代表客戶挑選的strategys對象,冒號后面的數字6表示在效驗過程中所必須驗證的參數,minLength:6的意思是效驗 registerForm.password 這個文本輸入框的value最小長度為6位;如果字符串中不包含冒號,說明效驗過程中不需要額外的效驗信息;
第三個參數是當效驗未通過時返回的錯誤信息;
當我們往validator對象里添加完一系列的效驗規則之后,會調用validator.start()方法來啟動效驗。如果validator.start()返回了一個errorMsg字符串作為返回值,說明該次效驗沒有通過,此時需要registerForm.onsubmit方法返回false來阻止表單提交。下面我們來看看初始化代碼如下:

var validateFunc = function(){
    var validator = new Validator(); // 創建一個Validator對象
    /* 添加一些效驗規則 */
    validator.add(registerForm.userName,'isNotEmpty','用戶名不能為空');
    validator.add(registerForm.password,'minLength:6','密碼長度不能小于6位');
    validator.add(registerForm.userName,'mobileFormat','手機號碼格式不正確');
    var errorMsg = validator.start(); // 獲得效驗結果
    return errorMsg; // 返回效驗結果
};
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;
        }
    },
    // 手機號碼格式
    mobileFormat: function(value,errorMsg) {
        if(!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
            return errorMsg;
        }
    }
};
var Validator = function(){
    this.cache = [];  // 保存效驗規則
};
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添加進參數列表
        str.push(errorMsg);  // 把errorMsg添加進參數列表
        return strategys[strategy].apply(dom,str);
    });
};
Validator.prototype.start = function(){
    for(var i = 0, validatorFunc; validatorFunc = this.cache[i++]; ) {
        var msg = validatorFunc(); // 開始效驗 并取得效驗后的返回信息
        if(msg) {
            return msg;
        }
    }
};

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

如上使用策略模式來編寫表單驗證代碼可以看到好處了,我們通過add配置的方式就完成了一個表單的效驗;這樣的話,那么代碼可以當做一個組件來使用,并且可以隨時調用,在修改表單驗證規則的時候,也非常方便,通過傳遞參數即可調用;
給某個文本輸入框添加多種效驗規則
上面的代碼我們可以看到,我們只是給輸入框只能對應一種效驗規則,比如上面的我們只能效驗輸入框是否為空,
validator.add(registerForm.userName,’isNotEmpty’,'用戶名不能為空’);但是如果我們既要效驗輸入框是否為空,還要效驗輸入框的長度不要小于10位的話,那么我們期望需要像如下傳遞參數:
validator.add(registerForm.userName,[{strategy:’isNotEmpty’,errorMsg:’用戶名不能為空’},{strategy: 'minLength:6',errorMsg:'用戶名長度不能小于6位'}])
我們可以編寫代碼如下:

// 策略對象
var strategys = {
    isNotEmpty: function(value,errorMsg) {
        if(value === '') {
            return errorMsg;
        }
    },
    // 限制最小長度
    minLength: function(value,length,errorMsg) {
        if(value.length < length) {
            return errorMsg;
        }
    },
    // 手機號碼格式
    mobileFormat: function(value,errorMsg) {
        if(!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
            return errorMsg;
        }
    }
};
var Validator = function(){
    this.cache = [];  // 保存效驗規則
};
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(); // 開始效驗 并取得效驗后的返回信息
    if(msg) {
        return msg;
    }
    }
};
// 代碼調用
var registerForm = document.getElementById("registerForm");
var validateFunc = function(){
    var validator = new Validator(); // 創建一個Validator對象
    /* 添加一些效驗規則 */
    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:'手機號格式不正確'},
    ]);
    var errorMsg = validator.start(); // 獲得效驗結果
    return errorMsg; // 返回效驗結果
};
// 點擊確定提交
registerForm.onsubmit = function(){
    var errorMsg = validateFunc();
    if(errorMsg){
        alert(errorMsg);
        return false;
    }
}

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 工廠模式類似于現實生活中的工廠可以產生大量相似的商品,去做同樣的事情,實現同樣的效果;這時候需要使用工廠模式。簡單...
    舟漁行舟閱讀 7,842評論 2 17
  • 單例模式 適用場景:可能會在場景中使用到對象,但只有一個實例,加載時并不主動創建,需要時才創建 最常見的單例模式,...
    Obeing閱讀 2,103評論 1 10
  • 介紹 策略模式的定義是:定義一系列的算法,把它們一個個封裝起來,并且使它們可以相互替換。 在程序設計中,我們也常常...
    悟空你又瘦了閱讀 617評論 0 2
  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,766評論 18 399
  • 不得不說,武則天賦予了那個時代獨特的勇氣,使女人們紛紛不安于室,開始熱衷于男人們的權利游戲,纖纖素手攪弄出朝堂風云...
    景小菡閱讀 1,002評論 13 24