1、策略模式
1.1 什么是策略模式
要实现某一个功能,有多种方案可以选择。我们定义策略,把它们一个个封装起来,并且使它们可以相互组合。
1.2 经典案例分析
需求:一个后台系统,需要通过权限来判断是否展示某些模块(用户是否为管理员,权限等级是否大于1,是否申请)
userData:{
idType:1 // 身份类型(0:普通用户1:管理员)
grade:1 // 权限等级
isShenqing:true // 是否申请
}
// 判断是否展示模块
function checkIdentity(val){
if(val.idType === 0)
{return false}
else if(val.grade <= 1 )
{return false}
else if(!val.isShenqing)
{return false}
return true
}
上面案例中是判断权限的常用的方式,根据所需条件,通过多次的if else来判断是否满足条件,这种方法非常直接,并且很容易通过代码实现。
但是如果此时权限判断又多了一种条件需要判断用户权限是否过期,如果是使用上方案例中方法实现的话,又需要在checkIdentity中新添加个if else
这样就暴露出问题了
1.checkIdentity方法会随着权限判断条件的增加,if else语句会变得越来越多。
2.每一次增加条件都会修改之前已经写好的checkIdentity方法,也就是说该方法无法复用
那么通过策略模式如何解决这个需求呢
一、拆解策略项
上方案例中三个条件,所以可以写出三个策略项,并且存储在策略集中
// 策略集
storageList:{
// 检查用户是否为管理员策略
checkIdType:function(value){
return value === 1
},
// 检查权限等级是否大于1策略
checkGrade:function(value){
return value >1
},
// 检查是否申请策略
checkIsShengqing:function(value){
return value
}
}
二、策略写好之后,如何将分散的策略组合起来呢
// 组合策略方法 这里接收两个参数,第一个是需要验证的值,第二个是验证值的策略
// 存储策略在缓存中
const cathe = []
function add(value,method){
this.cathe.push(function(){
return storageList[method](value)
})
}
三、验证最后策略返回值
function check(){
for(let i = 0; i<cathe.length; i++){
let checkIn = cathe[i]
const result = checkIn()
if(!result){
return false
}
}
return true
}
将代码组合到一个构造函数中,构成一个整体。这样每一个通过构造函数的实例可以互相独立,互不影响
var Validate = function(){
this.cathe = []
this.add = function(value,method){
this.cathe.push(function(){
return storageList[method](value)
})
}
this.check = function(){
for(let i = 0; i<this.cathe.length; i++){
let checkIn = this.cathe[i]
var result = checkIn()
if(!result){
return false
}
}
return true
}
}
上方案例通过策略模式就可以这样实现
function checkIdentity(data){
var validate = new Validate()
validate.add(data.idType,'checkIdType')
validate.add(data.checkGrade,'checkGrade')
validate.add(data.checkIsShengqing,'checkIsShengqing')
return validate.check()
}
之前通过简单的if else 实现的方法暴露出来的问题就能够得到解决
1.这里的checkIdentity方法通过添加策略项的方法替代了if else 的条件判断,使得代码结构清晰,易懂
2.这里的checkIdentity方法只要是类似的条件判断,完全可以在策略集中添加策略,可以按照自己的项目进行灵活扩展
1.3 策略模式使用场景
表单验证
表单验证是很常见的功能,现在我们一般都是使用el-form组件,通过传入规则对象以及prop来实现对应输入框的多种规则验证。我们也可以通过策略模式来模拟一下表单验证的功能
第一步:写出策略集
这里的策略方法是通过正则表达式,如果不通过就返回传进来的错误信息
// 表单验证策略集
const ruleIdentityList = {
isRequird: function (val, errMsg) {
if (val === '') {
return errMsg
}
},
isNumber: function (val, errMsg) {
if (!/[0-9]*/g.test(val)) {
return errMsg
}
},
isTel: function (val, errMsg) {
if (!/^1[3456789]\d{9}$/.test(val)) {
return errMsg
}
},
isIdCard: function (val, errMsg) {
if (!/(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/.test(val)) {
return errMsg
}
}
}
第二步:写出构造函数
// 构造函数
var Validator = function () {
this.cathe = []
// add 传入验证值,策略名,错误信息
this.add = function (value, method,errMsg) {
this.cathe.push(function () {
return ruleIdentityList[method](value,errMsg)
})
}
this.check = function () {
for (let i = 0; i < this.cathe.length; i++) {
let checkIn = this.cathe[i]
var result = checkIn()
if (result) {
console.log('result', result)
return result
}
}
return ''
}
}
第三步:导出调用方法
export function validator(rules,form){
let ruleArray = {}
Object.keys(form).forEach( key => {
// rules里没有form中的对象,表示不用验证
if (rules[key]) {
var validatorItem = new Validator()
console.log(key,rules[key])
rules[key].forEach(k => {
// 循环添加策略
validatorItem.add(form[key],k.met,k.errMsg)
})
// 将验证结果存入结果对象中
ruleArray[key] = validatorItem.check()
}
})
return ruleArray
}
总结:
优点: 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。
缺点: 1、策略类会增多。 2、所有策略类都需要对外暴露。