策略模式:定义一系列算法,把它们一个个封装起来,并且使它们可以相互替换(相同的目标和意图)
将不变的部分和可变的部分分隔开
问题代码
计算绩效 = 员工的工资数 * 对应的绩效
未重构前的写法,存在问题
- 包含比较多的
if条件语句 calculateBonus函数缺乏弹性,如果需要增加新的绩效模式,又要修改内部实现算法复用性差
// 最初写法var calculateBonus = function (performanceLevel, salary) {if (performanceLevel === 'S') return salary * 4if (performanceLevel === 'A') return salary * 3if (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 = nullthis.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 和算法分离。
