---- 淘宝优惠券 ----欢迎到论坛交流 ---域名问题某些图片和js资源无法访问,导致一些代码实例无法运行!(代码里gzui.net换成momen.vip即可)

设计模式-什么是策略模式(二)

前端开发 蚂蚁 35℃ 0评论

当我们计划国庆出去游玩时,在交通方式上,我们可以选择贵而快的飞机/价格中等但稍慢的动车/便宜但超级慢的火车,根据不同的人,选择对应的交通方式,且可以随意更换交通方式,这就是策略模式。

一、什么是策略模式

策略模式的定义是,定义一系列算法,把它们一个个封装起来,并且使它们可以相互替换。

二、实际场景

1. 计算年终奖

1.1 传统做法

有一个计算员工年终奖的需求,假设,绩效为 S 的员工年终奖是 4 倍工资,绩效为 A 的员工年终奖是 3 倍工资,绩效为 B 的员工年终奖是 2 倍工资,下面我们来计算员工的年终奖。

var calculateBonus = function(performanceLevel, salary) {
    if (performanceLevel === 'S') {
        return salary * 4;
    }
    if (performanceLevel === 'A') {
        return salary * 3;
    }
    if (performanceLevel === 'B') {
        return salary * 2;

    }
};



calculateBonus('B', 20000); // 输出:40000
 
calculateBonus( 'S', 6000 ); // 输出:24000

上述代码有以下缺点:

  • 使用 if-else 语句描述逻辑,代码庞大;
  • 缺乏弹性,如果需要修改绩效 S 的奖金系数,必须修改 calculateBonus 函数,违反了开放-封闭原则;
  • 无法再次复用,当其他地方需要用到这套逻辑,只能再复制一份。

1.2 策略模式做法

使用策略模式改良后

const strategies = {
    S: salary => {
        return salary * 4
    },
    A: salary => {
        return salary * 3
    },
    B: salary => {
        return salary * 2
    }
}

const calculateBonus = (level, salary) => {
    return strategies[level](salary)
}

console.log(calculateBonus('S', 20000))
console.log(calculateBonus('A', 10000))

可以看到上述代码做了以下改动:

  • 策略类 strategies 封装了具体的算法和计算过程(每种绩效的计算规则);
  • 环境类 calculateBonus 接受请求,把请求委托给策略类 strategies(员工的绩效和工资;
  • 将算法的使用和算法的实现分离,代码清晰,职责分明;
  • 消除大量的 if-else 语句。

1.3 小结

策略模式使代码可读性更高,易于拓展更多的策略算法。当绩效系数改变,或者绩效等级增加,我们只需要为 strategies 调整或新增算法,符合开放-封闭原则。

2. 表单校验

当网页上的表单需要校验输入框/复选框等等规则时,如何去实现呢?
现在有一个注册用户的表单需求,在提交表单之前,需要验证以下规则:

  • 用户名不能为空
  • 密码长度不能少于6位
  • 手机号码必须符合格式

2.1 传统做法

使用 if-else 语句判断表单输入是否符合对应规则,如不符合,提示错误原因。

<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
    <form id='registerForm' action="xxx" method="post">
        用户名:<input type="text" name="userName">
        密码:<input type="text" name="password">
        手机号:<input type="text" name="phone">
        <button>提交</button>
    </form>
    <script type="text/javascript">
        let registerForm = document.getElementById('registerForm')

        registerForm.onsubmit = () => {
                if (registerForm.userName.value) {
                        alert('用户名不能为空')
                        return false
                }

                if (registerForm.password.value.length < 6) {
                        alert('密码长度不能少于6')
                        return false
                }

                if (!/(^1[3|5|8][0-9]$)/.test(registerForm.phone.value)) {
                        alert('手机号码格式不正确')
                        return false
                }
        }
        </script>
</body>
</html>

上述代码有以下缺点:

  • onsubmit 函数庞大,包含大量 if-else 语句;
  • onsubmit 缺乏弹性,当有规则需要调整,或者需要新增规则时,需要改动 onsubmit 函数内部,违反开放-封闭原则;
    算法复用性差,只能通过复制,复用到其他表单。

2.2 策略模式做法

使用策略模式重构上述代码。

<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
    

    <form action="http://xxx.com/register" id="registerForm" method="post">
        
 请输入用户名:
        <input type="text" name="userName" />
        
 请输入密码:
        <input type="text" name="password" />
        
 请输入手机号码:
        <input type="text" name="phoneNumber" />
        <button>
            提交
        </button>
    </form>
    <script type="text/javascript" src="index.js">

    </script>
            
</body> 
 
</html>
// 表单dom
const registerForm = document.getElementById('registerForm')

// 表单规则
const rules = {
    userName: [
        {
            strategy: 'isNonEmpty',
            errorMsg: '用户名不能为空'
        },
        {
            strategy: 'minLength:10',
            errorMsg: '用户名长度不能小于10位'
        }   
    ],
    password: [
        {
            strategy: 'minLength:6',
            errorMsg: '密码长度不能小于6位'
        }
    ],
    phoneNumber: [
        {
            strategy: 'isMobile',
            errorMsg: '手机号码格式不正确'
        }
    ]
}

// 策略类
var strategies = {

    isNonEmpty: function(value, errorMsg) {

        if (value === '') {

            return errorMsg;

        }

    },
     minLength: function(value, errorMsg, length) {

        console.log(length)
        if (value.length < length) {

            return errorMsg;

        }

    },
     isMobile: function(value, errorMsg) {

        if (!/(^1[3|5|8][0-9]{9}$)/.test(value)) {

            return errorMsg;

        }

    }

};



// 验证类
const Validator = function () {
    this.cache = []
}

// 添加验证方法
Validator.prototype.add = function ({ dom, rules}) {
    rules.forEach(rule => {
        const { strategy, errorMsg } = rule
        console.log(rule)
        const [ strategyName, strategyCondition ] = strategy.split(':')
        console.log(strategyName)
        const { value } = dom
        this.cache.push(strategies[strategyName].bind(dom, value, errorMsg, strategyCondition))
    })
}

// 开始验证
Validator.prototype.start = function () {
    let errorMsg
    this.cache.some(cacheItem => {
            const _errorMsg = cacheItem()
            if (_errorMsg) {
                    errorMsg = _errorMsg
                    return true
            } else {
                    return false
            }
    })

    return errorMsg
}

// 验证函数
const validatorFn = () => {
    const validator = new Validator()
    console.log(validator.add)

    Object.keys(rules).forEach(key => {
        console.log(2222222, rules[key])
        validator.add({
            dom: registerForm[key],
            rules: rules[key]
        })
    })

    const errorMsg = validator.start()
    return errorMsg
}

// 表单提交
registerForm.onsubmit = () => {
    const errorMsg = validatorFn()
    if (errorMsg) {
        alert(errorMsg)
        return false
    }
    return false
}

上述代码通过 strategies 定义规则算法,通过 Validator 定义验证算法,将规则和算法分离,我们仅仅通过配置的方式就可以完成表单的校验,这些校验规则也可以复用在程序的任何地方,还能作为插件的形式,方便的被移植到其他项目中。

三、总结

策略模式是一种常用且有效的设计模式,通过上述例子,可以总结出策略模式的一些优点:

策略模式利用组合/委托和多态等技术和思想,可以有效的避免多重条件选择语句;
策略模式提供了对开放-封闭原则的完美支持,将算法封装中独立的策略类中,使得它们易于切换/理解/扩展;
在策略模式中利用组合和委托来让 Context 拥有执行算法的能力,这也是继承的一种更轻便的代替方案。

以上例子源码地址为:源码地址

https://codepen.io/yourmaster/pen/PopdpYY

基于策略模式简单实现element表单校验

作者:清汤饺子
链接:https://juejin.cn/post/6967669229902381064
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

转载请注明:有爱前端 » 设计模式-什么是策略模式(二)

喜欢 (0)or分享 (0)
发表我的评论
取消评论

表情