策略模式

啥時策略模式?

舉個例子,假如你從北京到杭州,可以選擇坐飛機去,還可以坐汽車去,還可以自駕去。

每個方法都可以去。

策略模式就是實現一個任務有多個方法,這些方法都可以相互替換。把這些方法封裝起來就是策略模式。

再舉個例子,年終獎發放,績效s的年終獎是工資的4倍。績效A的年終獎是工資的3倍。績效B的年終獎是工資的2倍。

js實現

var strategies={
    S: function (salary) {
       return salary * 4
    },
    A: function (salary) {
       return salary * 3
    },
    B: function (salary) {
       return salary * 2
    }
}

var caculateBouns = function (level,salary) {
  return strategies[level](salary)
}

console.log(caculateBouns('S',2000))
console.log(caculateBouns('A',3000))
console.log(caculateBouns('B',4000))

從定義上來看,策略模式就是用來封裝算法的。

來點實用的。常見的表單驗證如何用到策略模式呢?

假設我們正在編寫一個用戶註冊的頁面,在點擊註冊按鈕之前,有以下幾條校驗規則。

1 :用戶名不能爲空

2 :密碼長度不能少於6位

3: 手機號碼必須符合格式

var registForm = document.getElementById('registForm');
registForm.onSubmit = function(){
   if(registForm.userName.value === ''){
      alert('用戶名不能爲空')
      return false
   }
   if(registForm.password.value.length < 6){
      alert('密碼長度不能少於6位')
      return false
   }
   if(!/^1[3|5|8|7]\d{9}$/.test(registForm.phoneNumber.value)){
      alert('手機格式不正確')
      return false
   }
}

是不是你經常這麼寫?我也經常這麼寫。要想成爲一名更優秀的程序員就要時刻反思自己的代碼

那麼這段代碼有什麼缺點呢??

1 registForm.onsubmit函數比較龐大,包含了很多if語句

2 函數缺乏彈性,如果新增一條校驗規則,或者把密碼長度從6改爲8,我們就必須修改函數,這與開放-封閉原則相違背。

開放是對擴展開放,擴展功能是可以的。封閉是對修改封閉,對函數的內部修改是不提倡的,儘量不要去修改寫過的代碼。

3 算法的複用性差,如果項目中新增了另一個表單,這個表單也需要進行一些類似的校驗。那我們就只能複製這段代碼,這顯然不是上策。

使用策略模式來優化以上代碼

首先我們需要把這些校驗規則封裝成策略對象

// 策略類(包含哪些校驗規則)
var strategies = {
    isNotEmpty: 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|8|9|5]\d{9}$/.test(value)){
         return errorMsg
       }
    }
}
// 校驗容器
var Validator = function () {
  this.cache = []; // 緩存方法
}
validator.prototype.add=function(dom,rules){
  var self = this;
  for(let i =0,rule;rule = rules[i++]){
    let strategyAry = rule.strategy.split(':');
    let errorMsg = rule.errorMsg;
    self.cache.push(function(){
       var strategy = strategyAry.shift(); // 方法用於把數組的第一個元素從其中刪除,並返回第一個元素的值。
       startegyAry.unshift(dom.value) // 從頭加 
       startegyAry.push(errorMsg) // 從尾部加 [value,length,errorMsg]
       return strategies[strategy].apply(dom,startegyAry) 
    })
  }
}
Validator.prototype.start = function (){
   for(let i = 0,validatorFun;validatorFun = this.cache[i++]){
      var errMsg = validatorFun()
      if(errMsg){
        return errMsg
      }
   }
}
// 客戶端調用
var registForm = document.getElementById('registForm')
var validatorFun = function(){
   var validator = new Validator()
   validator.add(registForm.userName,[{
     startegy: 'isNotEmpty',
     errorMsg: '用戶名不能爲空'
   },{
     strategy: 'minLength: 10',
     errorMsg: '用戶名長度不能小於10'
   }])
   validator.add(registForm.password,[{
     strategy: 'minLength:8',
     errorMsg: '密碼不能少於6'
   }])
   validator.add(registForm.phoneNumber,[{
      strategy: 'isMobile',
      errorMsg: '手機格式不正確'
   }])
   var errorMsg = validator.start();
   return errorMsg
}


registForm.onSubmit= function(){
   var errMsg = validatorFun();
   if(errMsg){
     alert(errMsg)
     return false                              
   }
}

這樣一來,校驗規則只可以擴展,不需要再去修改已有的規則。還可以應用於所有的表單校驗。

 

總結: 在程序中使用策略模式就需要策略類,寫策略類就需要先知道所有的策略。這比把所有邏輯多起在一起使用if-else要好得多。

 

 

 

 

 

 

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