定義一系列算法,把它們一個個封裝成策略類,具體的算法封裝在策略類的內部方法裏,而且使這些策略類能夠互相替換。一個基於策略模式的設計至少由兩部分組成,第一部分是一組策略類,每一個策略類裏封裝了具體的算法。第二部分是環境類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
}
}
}
}
複製代碼
經過策略模式重構以後,只須要經過配置就能夠完成表單的校驗,這些校驗規則還能夠在任何地方複用。若是須要進行修改,好比改校驗規則或者提示,也是成本很低的。
優勢:
缺點: