博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Javascript设计模式(四)策略模式
阅读量:6850 次
发布时间:2019-06-26

本文共 4461 字,大约阅读时间需要 14 分钟。

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

在现实中,如果我们想去某个地方旅游,可以根据实际情况有多种路线

  • 如果没有时间但是不在乎钱,可以选择飞机
  • 如果没有钱,可以选择大巴活着火车
  • 如果再穷一点,可以选择骑自行车

使用策略模式计算奖金

现在以年终奖的计算为例

公司年终奖根据员工的工资基数年底绩效来发放

  • 绩效S,四倍年终奖
  • 绩效A,三倍年终奖
  • 绩效B,二倍年终奖

最初的实现

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', 2000) // 4000calculateBonus('S', 2000) // 8000

这段代码简单,但是存在显而易见的缺点

  1. 函数比较庞大,包含很多if-else语句,这些语句需要覆盖所有的逻辑分支
  2. 缺乏弹性,如果想新增绩效C,就得深入函数内部实现,违反开放-封闭原则
  3. 算法的复用性差

策略模式的实现

var strategies = {    "S": function(salary) {        return salary * 4    },    "A": function(salary) {        return salary * 3    },    "B": function(salary) {        return salary * 2    }                }var calculateBonus = function(level, salary) {    return strategies[level](salary)}console.log(calculateBonus('S', 2000)) // 8000console.log(calculateBonus('B', 2000)) // 4000

通过使用策略模式重构代码,消除来原程序中分支语句。所有计算奖金有关的逻辑分布在策略对象中,每个策略对象的算法已被各自封装在对象内部,当我们对这些策略对象发出“计算奖金”的请求时,它们会返回各自的计算结果,这不仅是多态性的体现,也是“自由交换”的目的。

使用策略模式实现缓动动画

            
我说div

用策略模式实现表单验证

从定义上看,策略模式就是用来封装算法的。但是如果仅仅用来封装算法,未免有点大材小用。在实际业务中,策略模式也可以用来封装一系列的“业务规则”。只要业务规则指向的目标一致,并且可以被替换使用,我们就可以用策略模式来封装它们。

普通版本的表单验证

    
请输入用户名
请输入密码
请输入手机号

这是一种很常见的编码方式,可以看到缺点和计算奖金一摸一样

用策略模式重构表单验证

  1. 很明显第一步我们需要将验证逻辑封装成策略对象
var strategies = {    isNonEmpty: function(value, errorMsg) {        if (value === '') {            return errorMsg        }    },    minLength: function(value, length, errorMsg) {        if (value.length < length) {            return errorMsg        }    },    isMobile: function(value, errorMsg) {        if (!/(^1[3|5|8][0-9]{9}$)/.test(value)) {            return errorMsg        }    }}
  1. 接下来实现Validator类,负责接受用户的请求并委托给strategies
var Validator = function() {    //保存校验规则    this.cache = [] }// 添加校验Validator.prototype.add = function(dom, rules) {    var self = this    // 遍历校验规则    for(var i = 0, rule; rule = rules[i++];) {         (function(rule){            //把strategy和参数分开            var strategyAry = rule.strategy.split(':')                 var errorMsg = rule.errorMsg                // 把校验的步骤用空函数包装起来,并且放入cache            self.cache.push(function(){                    // 挑选出校验规则                var strategy = strategyAry.shift()                // 把input的value添加进参数列表                strategyAry.unshift(dom.value)                        // 把errorMsg添加进参数列表                strategyAry.push(errorMsg)                            return strategies[strategy].apply(dom, strategyAry)            })        })(rule)    }}// 启动校验Validator.prototype.start = function() {    for (var i = 0, validatorFunc; validatorFunc = this.cache[i++];) {         // 开始校验,并取得校验后的结果        var errorMsg = validatorFunc()         if (errorMsg) {            return errorMsg        }    }}
  1. 接下来就是调用了
var registerForm = document.getElementById('registerForm')var validataFunc = function() {    var validator = new Validator()    validator.add(registerForm.username, [            {                strategy: 'isNonEmpty',                errorMsg: '用户名不能为空'            },            {                strategy: 'minLength:10',                errorMsg: '用户名长度不能小于10位'                                    }        ]    )    validator.add(registerForm.password, [            {                strategy: 'minLength:6',                errorMsg: '密码长度不能小于6位'                                    }        ]    )    validator.add(registerForm.phonenumber, [            {                strategy: 'isMobile',                errorMsg: '手机号码格式不正确'                                    }        ]    )                                    var errorMsg = validator.start()    return errorMsg                            }var sub = document.querySelector('input[type="submit"]')sub.onclick = function() {    var errorMsg = validataFunc()    if (errorMsg) {        console.error(errorMsg)        return false    }}

使用策略模式重构代码之后,我们不仅通过“配置”的方式就可以完成一个表单的校验,这些规则也可以复用在程序的任何地方,还能以插件的形式,方便地移植到其他项目中。并且新增或者修改规则也是毫不费力的。

策略模式的优缺点

优点

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

缺点

  1. 使用策略对象会增加很多策略类或者策略对象,但实际上比把这些逻辑放在Content更好。
  2. 策略模式会向用户暴露所有实现细节,这其实是违反最少知识原则。

转载地址:http://bugul.baihongyu.com/

你可能感兴趣的文章
你离BAT之间,只差这一套Java面试题
查看>>
laravel package 推荐,数据备份
查看>>
Synchronized锁在Spring事务管理下,为啥还线程不安全?
查看>>
环境变量PATH cp命令 mv命令 文档查看cat/more/less/head/tail
查看>>
阿里云亮相2019联通合作伙伴大会,边缘计算等3款云产品助力5G时代产业数字化转型...
查看>>
dubbo源码分析-服务端发布流程-笔记
查看>>
阿里云发布Apsara SA系列混合云存储阵列
查看>>
GoJS教程:链接模版
查看>>
QListWidget方式显示缩略图
查看>>
金三银四:蚂蚁金服JAVA后端面试题及答案之二面
查看>>
Ubuntu 外网不通解决方案
查看>>
OSChina 周六乱弹 —— 历史总是惊人的相似
查看>>
MySQL 大小写
查看>>
Lync 2013部署图片赏析-证书服务安装配置
查看>>
HTML5 本地缓存 (web存储)
查看>>
tomcat redis session共享(包含redis安全设置)
查看>>
iptables中DNAT、SNAT和MASQUERADE的作用
查看>>
kvm命令学习记录
查看>>
小菜鸡进阶之路-First week
查看>>
ORACLE 10g SYSAUX表空间快速增长之WRH$_ACTIVE_SESSION_HISTORY篇
查看>>