在生活中,我們去某個地方,可以走路,騎自行車,坐地鐵,坐火箭等等,方案很多。
策略模式的定義:定義一系列算法,把他們一個個封裝起來,並且使他們可以相互替換。
1,計算獎金
1.1,根據員工績效和工資來發獎金。
//策略模式:最初代碼實現
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))
缺點:
calculateBonus 裏全是if-else,所有的邏輯都在這裏面實現;
calculateBonus缺乏彈性,如果新增一個等級C,那麼就不得不修改calculateBonus的代碼
複用性差
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,實現一個普通的表單校驗功能
<!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" />
請輸入手機號:<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('手機號碼格式不正確')
return false
}
}
</script>
</body>
</html>
缺點很明顯:
所有的邏輯都在registerForm.onsubmit中,包含很多if-else語句
registerForm.onsubmit缺乏彈性
算法複用性差
2.2,策略模式重構表單校驗
(1)現在,我們要用策略模式來重構表單校驗。首先,我們要把校驗邏輯都封裝成策略對象:
// 策略基本完成
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)接下來我們來實現Validator類。Validator類在這裏作爲Context,負責接收用戶的請求並委託給strategy對象。先看一下Validator類對象是如何工作的,這樣便於我們編寫Validator類的代碼:
//新增方法,統一管理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', '手機號碼格式不正確')
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方法添加校驗規則,add方法有三個參數:
- registerForm.passWord:指參與校驗的input框
- 'minLength:6':以冒號隔開的字符串。冒號前面的minLength是校驗的策略名,6是需要的參數
- '密碼長度不能小於6位':校驗未通過時的錯誤信息
最後還需要一個start方法,用來對add進來的內容進行校驗,並返回錯誤信息。如果返回了錯誤信息,registerForm.onsubmit()方法返回false來阻止表單提交。
// Validator類在這裏作爲Context,負責接收用戶的請求併發送給strategies
var Validator = function () {
this.cache = []
}
// add中保存需要處理的函數
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,一個文本框可對應多個校驗規則
實際上2.2可以實現這個功能,只是不太好看
<!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" />
請輸入手機號:<input type="text" name="phoneNumber" />
<button>提交</button>
</form>
<script>
// 這裏主要是爲了在一個輸入框中匹配多個驗證規則;實際上之前的策略可以實現,多寫幾次就好了
/* 策略對象 */
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中保存需要處理的函數
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: '手機號碼格式不正確',
}])
var errorMsg = validator.start()
return errorMsg
}
registerForm.onsubmit = function () {
var errorMsg = validataFunc()
if (errorMsg) {
alert(errorMsg)
return false
}
}
</script>
</body>
</html>
3,策略模式的優缺點
優點:
- 策略模式利用組合、委託、多態等技術和思想,可以有效地避免多重條件選擇語句
- 策略模式提供了對開放-封閉原則的完美支持,將算法封裝在獨立的strategy中,使得他們易於切換,易於理解,易於擴展
- 複用性強
缺點: - 用戶在使用策略模式時,需要了解其中所有的細節,strategy要暴露所有實現,違反最少知識原則