啥時策略模式?
舉個例子,假如你從北京到杭州,可以選擇坐飛機去,還可以坐汽車去,還可以自駕去。
每個方法都可以去。
策略模式就是實現一個任務有多個方法,這些方法都可以相互替換。把這些方法封裝起來就是策略模式。
再舉個例子,年終獎發放,績效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要好得多。