策略模式:定义一系列算法,把它们一个个封装起来,并且使它们可以相互替换(相同的目标和意图)
将不变的部分和可变的部分分隔开
问题代码
计算绩效 = 员工的工资数 * 对应的绩效
未重构前的写法,存在问题
- 包含比较多的
if条件语句
calculateBonus
函数缺乏弹性,如果需要增加新的绩效模式,又要修改内部实现算法复用性
差
// 最初写法
var calculateBonus = function (performanceLevel, salary) {
if (performanceLevel === 'S') return salary * 4
if (performanceLevel === 'A') return salary * 3
if (performanceLevel === 'B') return salary * 2
}
传统实现
**
// 定义一系列的算法,各自封装成策略类,算法被封装在策略类内部的方法
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) // 把计算奖金的操作委托给相应的策略对象
}
var bonus = new Bonus()
bonus.setSalary(10000)
bonus.setStrategy(new performanceS())
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)
}
校验表单
表单的校验逻辑有:
- 用户名不能为空
- 密码长度不能少于6
- 手机号必须符合格式
常见模式
/**
* 常见的使用条件判断方法
*/
var registerForm = document.getElementById('register')
registerForm.onsubmit = function () {
if (registerForm.username.value === '') {
console.log('用于名不能空')
return false
}
if (registerForm.password.value.length < 6) {
console.log('密码长度不能少于6')
return false
}
if (!/(^1[3|5|8|7|4][0-9]{9}$)/.test(registerForm.phoneNumber.value)) {
console.log('手机号码格式不正确')
return false
}
}
使用策略模式:
// 提取校验逻辑算法,封装为策略对象
const strategies = {
isNonEmpty(value, errorMsg) {
if (value === '') {
return errorMsg
}
},
minLength(value, length, errorMsg) {
if (value.length < length) {
return errorMsg
}
},
isMobile(value, errorMsg) {
if (!/(^1[3|5|8|7|4][0-9]{9}$)/.test(value)) {
return errorMsg
}
}
}
// Validator类作为Context,负责接收用户的请求并委托给策略对象进行处理
const Validator = function () {
this.cache = [] //保存校验规则
}
Validator.prototype.add = function (dom, rule, errorMsg) {
var ary = rule.split(':') // 把stragy和参数分开
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, validatorFunc; validateFunc = this.cache[i++];) {
var msg = validateFunc()
if (msg) {
return msg
}
}
}
// 调用
const validateFunc = function () {
const validator = new Validator()
validator.add(registerForm.username, 'isNonEmpty', ' 用户名不能为空')
validator.add(registerForm.password, 'minLength:6', '密码长度不能少于6位')
validator.add(registerForm.phoneNumber, 'isMobile', '手机号码格式不正确')
const errorMsg = validator.start()
return errorMsg
}
总结
策略模式具有的
优点:
- 策略模式利用组合、委托和多态等技术和思想,可以有效避免多重条件选择语句
- 提供了对开放-封闭原则的完美支持,将算法独立封装。便于理解、扩展
- 封装的算法能复用
什么时候会用到策略模式,当出现使用重复的 if-else
等条件语句时,便可考虑使用策略模式,将 context
和算法分离。