策略模式

98 阅读1分钟

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、所有策略类都需要对外暴露。