定义一系列算法,把它们一个个封装成策略类,具体的算法封装在策略类的内部方法里,而且使这些策略类能够互相替换。一个基于策略模式的设计至少由两部分组成,第一部分是一组策略类,每一个策略类里封装了具体的算法。第二部分是环境类Context,Context主要接受客户的请求,而后把请求委托给某一个策略类。javascript
下面主要经过两个具体的案例来介绍策略类的使用。java
不少公司都设有年终奖,并且年终奖通常跟员工的月工资基数和绩效有关。若是绩效为S,发4个月年终奖;绩效A发3个月;绩效为B,发2个月。用代码计算奖金以下:算法
const calculateBonus = function (performanceLevel, salary) {
if (performanceLevel === 'S') {
return salary * 4
}
if (performanceLevel === 'A') {
return salary * 3
}
if (performanceLevel === 'B') {
return salary * 2
}
}
复制代码
用简单的代码配合if语句就能实现该功能,可是却有不少的缺点。设计模式
下面使用策略模式重构代码。第一步,定义策略类:app
// S
class PerformanceS {
construct (salary) {
this.salary = salary
}
calculate () {
return this.salary * 4
}
}
// A
class PerformanceA {
construct (salary) {
this.salary = salary
}
calculate () {
return this.salary * 3
}
}
// B
class PerformanceB {
construct (salary) {
this.salary = salary
}
calculate () {
return this.salary * 2
}
}
复制代码
第二步,定义环境类,Bonus:dom
class Bonus {
construct (salary) {
this.salary = salary
}
setStrategy (Strategy) {
this.strategy = new Strategy(this.salary)
}
getBonus () {
return this.strategy.calculate()
}
}
复制代码
使用:函数
const bonus = new Bonus(7000)
bonus.setStrategy(PerformanceS)
bonus.getBonus() // 28000
复制代码
重构完以后,咱们发现代码更加清晰,每一个类的职责也更加鲜明。ui
上面咱们是用面向对象的方式来实现策略模式,在JavaScript中,函数也是对象,因此更加直接的作法是:this
const strategies = {
'S': function (salary) {
return salary * 4
},
'A': function (salary) {
return salary * 3
},
'B': function (salary) {
return salary * 2
}
}
const calculateBonus = function (performanceLevel, salary) {
return strategies[performanceLevel](salary)
}
复制代码
策略模式中的策略类主要是用来封装算法的,可是若是只封装算法,有点大材小用。在实际开发中,咱们能够把算法定义变得更广,好比一系列的业务规则,这些业务规则的目标一致,而且能够被相互替换使用。下面经过策略模式来编写一个表单校验的例子。
需求是这样的,咱们在编写一个注册的页面,在点击注册按钮以前,有以下几条校验规则:spa
第一个版本
const registerForm = document.getElementById('registerForm')
registerForm.onsubmit = function () {
if (registerForm.userName.value === '') {
alert('用户名不能为空')
return
}
if (registerForm.password.value.length < 6) {
alert('用户密码不能少于6位')
return
}
if (!/(^1[3|5|7|8][0-9]{9}$)/.test(registerForm.phone.value)) {
alert('用户手机格式不正确')
return
}
}
复制代码
它的缺点跟计算奖金的第一个版本差很少,缺少弹性,违反开闭原则,复用性差。
用策略模式重构
定义校验规则的策略:
const strategies= {
isNonEmpty (value, errMsg) {
if (value === '') {
return errMsg
}
},
minLength (value, length, errMsg) {
if (value.length < length) {
return errMsg
}
},
isMobile (value, errMsg) {
if (!/(^1[3|5|7|8][0-9]{9}$)/.test(value)) {
return errMsg
}
}
}
复制代码
先看具体怎么使用Validator:
const validatorFunc = function () {
const validator = new Validator()
// 添加校验规则
validator.add(registerForm.userName, 'isNonEmpty', '用户名不能为空')
validator.add(registerForm.password, 'minLength:6', '用户密码不能少于6位')
validator.add(registerForm.mobile, 'isMobile', '用户手机格式不正确')
const errMsg = validator.start() // 得到校验结果
return erMsg
}
const registerForm = document.getElementById('registerForm')
registerForm.onsubmit = function () {
const errMsg = validatorFunc()
if (errMsg) {
alert(errMsg)
return
}
}
复制代码
定义环境类Validator类:
class Validator {
static rules = []
add (dom, rule, errMsg) {
const ary = rule.split(':')
this.rules.push(function () {
const rule = ary.shift()
ary.unshift(dom.value)
ary.push(errMsg)
return strategies[rule].apply(dom, ary)
})
}
start () {
for (let i = 0; i < this.rules.length; i++) {
const validatorFunc = this.rules[i]
const errMsg = validatorFunc()
if (errMsg) {
return errMsg
}
}
}
}
复制代码
经过策略模式重构以后,只须要经过配置就能够完成表单的校验,这些校验规则还能够在任何地方复用。若是须要进行修改,好比改校验规则或者提示,也是成本很低的。
优势:
缺点: