资讯专栏INFORMATION COLUMN

Javascript设计模式(四)策略模式

U2FsdGVkX1x / 3400人阅读

摘要:策略模式的优缺点优点策略模式利用组合,委托和多态等技术思想,可以有效避免多重条件选择语句。策略模式提供了对开放封闭原则的完美支持。策略模式会向用户暴露所有实现细节,这其实是违反最少知识原则。

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

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

如果没有时间但是不在乎钱,可以选择飞机

如果没有钱,可以选择大巴活着火车

如果再穷一点,可以选择骑自行车

使用策略模式计算奖金

现在以年终奖的计算为例

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

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

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

函数比较庞大,包含很多if-else语句,这些语句需要覆盖所有的逻辑分支

缺乏弹性,如果想新增绩效C,就得深入函数内部实现,违反开放-封闭原则

算法的复用性差

策略模式的实现
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)) // 8000
console.log(calculateBonus("B", 2000)) // 4000

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

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


    
        
        
        
    
    
    
我说div
用策略模式实现表单验证

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

普通版本的表单验证



    
    
    


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

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

用策略模式重构表单验证

很明显第一步我们需要将验证逻辑封装成策略对象

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
        }
    }
}

接下来实现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
        }
    }
}

接下来就是调用了

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
    }
}

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

策略模式的优缺点 优点

策略模式利用组合,委托和多态等技术思想,可以有效避免多重条件选择语句。

策略模式提供了对开放-封闭原则的完美支持。将算法封装在独立的strategy中,使得它们易于切换,易于理解,易于扩展。

策略模式中的算法也可以复用在系统中的其他地方。

在策略模式中利用组合和委托让Content拥有执行算法的能力,这也是继承的一种更轻便的替代方案。

缺点

使用策略对象会增加很多策略类或者策略对象,但实际上比把这些逻辑放在Content更好。

策略模式会向用户暴露所有实现细节,这其实是违反最少知识原则。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/99180.html

相关文章

  • 前端也要学系列:设计模式策略模式

    摘要:做前端开发已经好几年了,对设计模式一直没有深入学习总结过。今天第一天,首先来讲策略模式。什么是策略模式四兄弟的经典设计模式中,对策略模式的定义如下定义一系列的算法,把它们一个个封装起来,并且使它们可互相替换。 做前端开发已经好几年了,对设计模式一直没有深入学习总结过。随着架构相关的工作越来越多,越来越能感觉到设计模式成为了我前进道路上的一个阻碍。所以从今天开始深入学习和总结经典的设计模...

    Anchorer 评论0 收藏0
  • JavaScript系列() - 收藏集 - 掘金

    摘要:函数式编程前端掘金引言面向对象编程一直以来都是中的主导范式。函数式编程是一种强调减少对程序外部状态产生改变的方式。 JavaScript 函数式编程 - 前端 - 掘金引言 面向对象编程一直以来都是JavaScript中的主导范式。JavaScript作为一门多范式编程语言,然而,近几年,函数式编程越来越多得受到开发者的青睐。函数式编程是一种强调减少对程序外部状态产生改变的方式。因此,...

    cfanr 评论0 收藏0
  • JavaScript 模式》知识点小抄本(上)

    摘要:单体模式有以下优点用来划分命名空间,减少全局变量数量。通常我们使用操作符创建单体模式的三种选择,让构造函数总返回最初的对象使用全局对象来存储该实例不推荐,容易全局污染。实现该工厂模式并不困难,主要是要找到能够穿件所需类型对象的构造函数。 介绍 最近开始给自己每周订个学习任务,学习结果反馈为一篇文章的输出,做好学习记录。 这一周(02.25-03.03)我定的目标是《JavaScri...

    didikee 评论0 收藏0
  • JavaScript设计模式----策略模式

    摘要:实际上在这种将函数作为一等对象的语言里,策略模式已经融入到了语言本身当中,我们经常使用高阶函数来封装不同的行为,并且把它传递到另一个函数中。 声明:这个系列为阅读《JavaScript设计模式与开发实践》 ----曾探@著一书的读书笔记 1.策略模式的定义 将不变的部分和变化的部分隔开是每个设计模式的主题。 定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。 2.策略模式...

    forrest23 评论0 收藏0
  • JavaScript 设计模式(二):策略模式

    摘要:策略模式实现的也是类似的场景。第二个部分是环境类不变,接收客户的请求,随后把请求委托给某一个策略类。参考文章设计模式设计模式与开发实践设计模式系统讲解与应用本文首发,期待作者以乐之名本文原创,有不当的地方欢迎指出。 showImg(https://segmentfault.com/img/bVbugi7?w=800&h=600); 策略模式:定义一系列的算法,把它们一个个封装起来,并且...

    荆兆峰 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<