在生活中,我們?nèi)ツ硞€地方,可以走路,騎自行車,坐地鐵,坐火箭等等,方案很多。
策略模式的定義:定義一系列算法,把他們一個個封裝起來,并且使他們可以相互替換。
1,計算獎金
1.1,根據(jù)員工績效和工資來發(fā)獎金。
//策略模式:最初代碼實現(xiàn)
var calculateBonus = function (level, salary) {
if (level === 'S') {
return salary * 5
}
if (level === 'A') {
return salary * 3
}
if (level === 'B') {
return salary * 2
}
}
console.log(calculateBonus('S', 4000))
console.log(calculateBonus('B', 2000))
缺點(diǎn):
calculateBonus 里全是if-else,所有的邏輯都在這里面實現(xiàn);
calculateBonus缺乏彈性,如果新增一個等級C,那么就不得不修改calculateBonus的代碼
復(fù)用性差
1.2,javaScript版策略模式
// js版本的策略模式->1.html
var strategies = {
'S': function (salary) {
return salary * 5
},
'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', 4000))
console.log(calculateBonus('B', 2000))
2,表單校驗
2.1,實現(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://test.com/register" method='post' id="registerForm">
請輸入用戶名:<input type="text" name="userName" />
請輸入密碼:<input type="text" name="passWord" />
請輸入手機(jī)號:<input type="text" name="phoneNumber" />
<button>提交</button>
</form>
<script>
var registerForm = document.getElementById('registerForm')
registerForm.onsubmit = function () {
if (registerForm.userName.value === '') {
alert('用戶名不能為空')
return false
}
if (registerForm.passWord.value.length < 6) {
alert('密碼長度不能小于6位')
return false
}
if (!/(^1[3|5|8][0-9]{9}$)/.test(registerForm.phoneNumber.value)) {
alert('手機(jī)號碼格式不正確')
return false
}
}
</script>
</body>
</html>
缺點(diǎn)很明顯:
所有的邏輯都在registerForm.onsubmit中,包含很多if-else語句
registerForm.onsubmit缺乏彈性
算法復(fù)用性差
2.2,策略模式重構(gòu)表單校驗
(1)現(xiàn)在,我們要用策略模式來重構(gòu)表單校驗。首先,我們要把校驗邏輯都封裝成策略對象:
// 策略基本完成
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
}
},
}
(2)接下來我們來實現(xiàn)Validator類。Validator類在這里作為Context,負(fù)責(zé)接收用戶的請求并委托給strategy對象。先看一下Validator類對象是如何工作的,這樣便于我們編寫Validator類的代碼:
//新增方法,統(tǒng)一管理form
var validataFunc = function () {
var validator = new Validator()
validator.add(registerForm.userName, 'isNonEmpty', '用戶名不能為空')
validator.add(registerForm.passWord, 'minLength:6', '密碼長度不能小于6位')
validator.add(registerForm.phoneNumber, 'isMobile', '手機(jī)號碼格式不正確')
var errorMsg = validator.start()
return errorMsg
}
var registerForm = document.getElementById('registerForm')
registerForm.onsubmit = function () {
var errorMsg = validataFunc()
if (errorMsg) {
alert(errorMsg)
return false
}
}
(3)可以看到,validator對象時通過add方法添加校驗規(guī)則,add方法有三個參數(shù):
- registerForm.passWord:指參與校驗的input框
- 'minLength:6':以冒號隔開的字符串。冒號前面的minLength是校驗的策略名,6是需要的參數(shù)
- '密碼長度不能小于6位':校驗未通過時的錯誤信息
最后還需要一個start方法,用來對add進(jìn)來的內(nèi)容進(jìn)行校驗,并返回錯誤信息。如果返回了錯誤信息,registerForm.onsubmit()方法返回false來阻止表單提交。
// Validator類在這里作為Context,負(fù)責(zé)接收用戶的請求并發(fā)送給strategies
var Validator = function () {
this.cache = []
}
// add中保存需要處理的函數(shù)
Validator.prototype.add = function (dom, rule, errorMsg) {
this.cache.push(function () {
var array = rule.split(':')
var strategy = array.shift()
array.unshift(dom.value)
array.push(errorMsg)
return strategies[strategy].apply(dom, array)
})
}
Validator.prototype.start = function () {
for (var i = 0, validatorFunc; validatorFunc = this.cache[i++];) {
var errorMsg = validatorFunc()
if (errorMsg) {
return errorMsg
}
}
}
2.3,一個文本框可對應(yīng)多個校驗規(guī)則
實際上2.2可以實現(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://test.com/register" method='post' id="registerForm">
請輸入用戶名:<input type="text" name="userName" />
請輸入密碼:<input type="text" name="passWord" />
請輸入手機(jī)號:<input type="text" name="phoneNumber" />
<button>提交</button>
</form>
<script>
// 這里主要是為了在一個輸入框中匹配多個驗證規(guī)則;實際上之前的策略可以實現(xiàn),多寫幾次就好了
/* 策略對象 */
var strategies = {
isNonEmpty: function (value, errorMsg) {
if (value === '') {
console.log(errorMsg)
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 () {
this.cache = []
}
// add中保存需要處理的函數(shù)
Validator.prototype.add = function (dom, rules) {
var self = this
for (let i = 0, rule; rule = rules[i]; i++) {
(function (rule) {
self.cache.push(function () {
var startegyArr = rule.startegy.split(':')
var startegy = startegyArr.shift()
startegyArr.unshift(dom.value)
startegyArr.push(rule.errorMsg)
return strategies[startegy].apply(dom, startegyArr)
})
})(rule)
}
}
// 上面寫法可以改寫成這個
// Validator.prototype.add = function (dom, rules) {
// var self = this
// for (let i = 0; i < rules.length; i++) {
// self.cache.push(function () {
// var rule = rules[i]
// var startegyArr = rule.startegy.split(':')
// var startegy = startegyArr.shift()
// startegyArr.unshift(dom.value)
// startegyArr.push(rule.errorMsg)
// return strategies[startegy].apply(dom, startegyArr)
// })
// }
// }
Validator.prototype.start = function () {
for (var i = 0, validatorFunc; validatorFunc = this.cache[i++];) {
var errorMsg = validatorFunc()
if (errorMsg) {
return errorMsg
}
}
}
/* 用戶代碼 */
var registerForm = document.getElementById('registerForm')
var validataFunc = function () {
var validator = new Validator()
validator.add(registerForm.userName, [{
startegy: 'isNonEmpty',
errorMsg: '用戶名不能為空',
}, {
startegy: 'minLength:3',
errorMsg: '密碼長度不能小于3位',
}])
validator.add(registerForm.passWord, [{
startegy: 'minLength:6',
errorMsg: '密碼長度不能小于6位',
}])
validator.add(registerForm.phoneNumber, [{
startegy: 'isMobile',
errorMsg: '手機(jī)號碼格式不正確',
}])
var errorMsg = validator.start()
return errorMsg
}
registerForm.onsubmit = function () {
var errorMsg = validataFunc()
if (errorMsg) {
alert(errorMsg)
return false
}
}
</script>
</body>
</html>
3,策略模式的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
- 策略模式利用組合、委托、多態(tài)等技術(shù)和思想,可以有效地避免多重條件選擇語句
- 策略模式提供了對開放-封閉原則的完美支持,將算法封裝在獨(dú)立的strategy中,使得他們易于切換,易于理解,易于擴(kuò)展
- 復(fù)用性強(qiáng)
缺點(diǎn): - 用戶在使用策略模式時,需要了解其中所有的細(xì)節(jié),strategy要暴露所有實現(xiàn),違反最少知識原則