策略模式的定义是:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换
使用策略模式计算奖金
策略模式有着广泛的应用。本节我们就以年终奖的计算为例进行介绍。
很多公司的年终奖是根据员工的工资基数和年底绩效情况来发放的。例如,绩效为 S 的人年 终奖有 4 倍工资,绩效为 A 的人年终奖有 3 倍工资,而绩效为 B 的人年终奖是 2 倍工资。假设财 务部要求我们提供一段代码,来方便他们计算员工的年终奖。
if…else…
- 这个代码很简单,但是会包含很多的if…else…
- 代码复用性很差
代码
```javascript var calculateBonus = function (level, salary) { if (level === ‘s’) { return salary 4 } if (level === ‘a’) { return salary 3 } if (level === ‘b’) { return salary * 2 } }
calculateBonus(‘s’, 20000) // 80000 calculateBonus(‘b’, 10000) // 20000
<a name="u0yji"></a>
#### 使用组合函数重构代码
```javascript
var performanceS = function(salary){
return salary * 4
}
var performanceA = function(salary){
return salary * 3
}
var performanceB = function(salary){
return salary * 2
}
var calculateBonus = function (level, salary) {
if (level === 's') {
return performanceS(salary)
}
if (level === 'a') {
return performanceA(salary)
}
if (level === 'b') {
return performanceB(salary)
}
}
- calculateBonus 函数有可能越来越庞大,而且在系统变化的时候缺乏弹性
策略模式实现
var PerformanceS = function () {}
performanceS.prototype.calculate = function (salary) {
return salary * 4
}
var performanceA = function () {}
performanceA.prototype.calculate = function (salary) {
return salary * 3
}
var performanceB = function () {}
performanceB.prototype.calculate = function (salary) {
return salary * 2
}
// 接下来定义奖金类
var Bonus = function () {
this.salary = null // 原始工资
this.strategy = null // 绩效工资对应的策略对象
}
Bonus.prototype.setSalary = function (salary) {
this.salary = salary // 设置原始工资
}
Bonus.prototype.setStrategy = function (strategy) {
this.strategy = strategy // 绩效工资对应的策略对象
}
Bonus.prototype.getBonus = function () {
return this.strategy.calculate(this.salary) // 把计算奖金的操作,委托给策略对象
// 。。。。有一点java抽象类的概念,需要strategy必须实现calculate方法
}
JavaScript中的策略模式
var strategies = {
S: function (salary) {
return salary * 4
},
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', 20000))
表单验证
原始代码
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
}
}
优化后代码
规则
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
}
},
}
验证对象 ```javascript var Validator = function () { this.cache = [] //保存校验规则 }
Validator.prototype.add = function (dom, rule, errorMsg) { var ary = rule.split(‘:’) this.cache.push(function () { var strategy = ary.shift() ary.unshift(dom.value) ary.push(errorMsg) return strategies[strategy].apply(dom, ary) }) }
Validator.prototype.start = function () { for (var i = 0, validataFunc; (validataFunc = this.cache[i++]); ) { var msg = validataFunc() if (msg) { return msg } } }
- 用法
```javascript
var validataFunc = function () {
var validator = new Validator() // 创建一个 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() // 如果 errorMsg 有确切的返回值,说明未通过校验
if (errorMsg) {
alert(errorMsg)
return false // 阻止表单提交
}
}