JavaScript 策略模式详解:从奖金计算到动画与表单验证

作者:竹影时间:2026-10-24 21:50:02浏览:534

本文深入解析 JavaScript 中的策略模式,通过奖金计算、缓动动画和表单验证三个实例,展示如何将一系列算法封装并相互替换。文章对比了传统分支语句与策略模式的优劣,重点介绍利用对象字面量实现策略模式的方法,帮助开发者提升代码的可维护性与扩展性。

一. 认识策略模式

策略模式的核心定义在于:将一系列算法分别封装起来,使得它们之间可以相互替换。

作为开发中极为常见的设计模式之一,策略模式主要由两部分构成:一是策略类,负责封装各种具体且相似的算法;二是环境类,负责接收客户端请求,并将这些请求委托给相应的策略类处理。通俗来说,策略模式就是将具有相同算法逻辑的函数封装在一个容器中,并通过统一的方式调用它们。接下来,我们将通过代码实例来深入理解这一模式。

二. 具体实现和思想

假设我们需要编写一个计算员工奖金的程序:绩效等级为 S 时发放基本工资 4 倍的奖金,A 级发放 3 倍,以此类推。在常规实现中,我们通常会使用条件判断语句来完成这一逻辑。

1. 通过分支实现

        let bonus = function (performance, salary) {
            if(performance === "S") {
                return salary*4;
            }
            if(performance === "A") {
                return salary*3;
            }
            if(performance === "B") {
                return salary*2;
            }
        }

分析:这种实现方式存在明显的缺陷。随着绩效等级的增加(例如新增 C、D、E 等级),if 分支会不断累积,导致代码体积膨胀,维护成本随之升高。

因此,我们引入策略模式对代码进行重构。

2. 使用策略模式实现

        let performanceS = function () {};
        performanceS.prototype.calculate = function ( salary ) {
            return salary*4
        }
        let performanceA = function () {};
        performanceA.prototype.calculate = function ( salary ) {
            return salary*3
        }
        let performanceB = function () {};
        performanceB.prototype.calculate = function ( salary ) {
            return salary*2
        }
        let performanceC = function () {};
        performanceC.prototype.calculate = function ( salary ) {
            return salary*1
        }
 
        let Bonus = function () {
            this.salary = null; // 原始工资
            this.strategy = null; // 原始绩效
        }
        Bonus.prototype.setSalary = function ( salary ) {
            this.salary = salary;
        }
        Bonus.prototype.setStrategy = function ( strategy ) {
            this.strategy = strategy;
        }
        Bonus.prototype.getBonus = function () {
            if(!this.strategy) {
                throw new Error("未设置绩效");
            }
            return this.strategy.calculate(this.salary);
        }
 
        let bonus = new Bonus();
        bonus.setSalary(10000);
        bonus.setStrategy(new performanceS());
        console.log(bonus.getBonus());

分析:重构后,我们将每种绩效对应的算法独立封装为函数。在计算奖金时,只需将相应的绩效函数传入 getBonus 函数即可。这种方式不仅消除了冗长的 if 分支,降低了运行时的性能消耗,还提升了代码的弹性,使得未来增加新的绩效等级时无需修改原有代码。

主要思想:上述代码基于面向对象语言,引入了多态的概念,但在 JavaScript 中并不完全适用。

3. JavaScript 版本的策略模式

        // js中函数也是对象,直接将 strategy 定义为函数
        let strategy = {
            "S": function ( salary ){
                return salary*4;
            },
            "A": function ( salary ) {
                return salary*3;
            },
            "B": function ( salary ) { 
                return salary*2;
            }
        }
        let calculateBonus = function ( level, salary ) {
            return strategy[ level ]( salary );
        }
        console.log(calculateBonus('A', 20000)) // 6000

分析:在 JavaScript 中,函数本身也是对象,我们可以直接将 strategy 定义为对象字面量,将函数封装其中。这样做使得代码结构更加清晰简洁,同时增强了代码的复用性和扩展性。

以上就是 JavaScript 设计模式中策略模式的核心思想与实现方式。在实际应用中,策略模式主要有两个典型场景:一是用于实现缓动动画效果,二是用于实现表单验证逻辑。有兴趣的读者可以继续往下看。

三. 策略模式的实际运用

1. 使用策略模式实现缓存动画

        // 缓动算法
        let tween = {
            linear (t, b, c, d) {
                return c*t/d + b;
            },
            easeIn (t, b, c, d) {
                return c*(t /= d) *t + b;
            },
            strongEaseIn (t, b, c, d) {
                return c*(t /= d) *t *t *t *t + b;
            }
        }
 
        // 定义一个动画类,参数为要运动的 dom 节点
        let Animate = function ( dom ) {
            this.dom = dom;
            this.startTime = 0;
            this.startPos = 0;
            this.endPos = 0;
            this.propertyName = null;
            this.easing = null; // 缓动算法
            this.duration = null;
        }
 
        // 启动方法
        Animate.prototype.start = function (propertyName, endPos, duration, easing) {
            this.startTime =+ new Date;
            this.startPos = this.dom.getBoundingClientRect()[propertyName]; // dom 初始位置
            this.propertyName = propertyName;
            this.endPos = endPos;
            this.duration = duration;
            this.easing = tween[easing];
 
            let self = this;
            let timeId = setInterval(() => {
                if( self.step() === false){
                    clearInterval(timeId);
                }
            }, 19);
        }
 
        // 实现小球每一帧要做的事情
        Animate.prototype.step = function () {
            let t =+ new Date;
            if(t>this.startTime + this.duration){
                this.update(this.endPos);
                return false;
            }
            let pos = this.easing(t - this.startTime, this.startPos, this.endPos - this.startPos, this.duration);
            this.update(pos);
        }
 
        Animate.prototype.update = function (pos) {
            this.dom.style[this.propertyName] = pos + 'px';
        }
 
        let test = function () {
            let div = document.getElementById('div');
            let animate = new Animate(div);
            animate.start('left', 500, 1000, 'strongEaseIn');
            // animate.start('top', 1500,  500, 'strongEaseIn');
        }
        test();

2. 使用策略模式进行表单验证

        let strategies = {
            isNonEmpty ( value, errorMsg) { // 判断是否为空
                if(value === '') {
                    return errorMsg;
                }
            },
            minLength (value, length, errorMsg){
                if (value.length < length) {
                    return errorMsg;
                }
            }
        }
 
        let dom = document.forms[0].acount;
 
        let validatarFunc = function () {
            let validator = new Validator();
            // 添加校验规则
            validator.add(dom, 'isNonEmpty', '用户名不能为空!');
            let errorMsg = validator.start();
            return errorMsg; // 返回校验结果
        }
        
 
        // 实现表单校验保存类
        let Validator = function () {
            this.cache = []; // 保存校验规则
        }
        Validator.prototype.add = function (dom, rule, errorMsg) {
            let ary = rule.split(':');
            this.cache.push( function(){
                let strategy = ary.shift();
                ary.unshift(dom.value);
                ary.push( errorMsg );
                return strategies[strategy].apply(dom, ary);
            })
        }
        Validator.prototype.start = function () {
            for(let i = 0, validatorFunc; validatorFunc = this.cache[i++];){
                let msg = validatorFunc();
                if( msg ) {
                    return msg;
                }
            }
        }
 
        document.forms[0].addEventListener('submit', (e) =>{
            let errorMsg = validatarFunc();
            if(errorMsg){
                alert(errorMsg);
                e.preventDefault();
            }
        })

分析:在第一个应用场景中,我们将缓动算法封装在一个对象中,调用时便于相互替换,同时也方便后续扩展。

在第二个应用场景中,我们将各种校验规则封装起来,以便灵活调用。

四. 总结

策略模式利用组合、委托和多态等技术思想,有效地避免了复杂的多重条件选择语句。它将算法封装在 strategy 对象中,使得算法的切换和扩展变得简单容易。

本文到此结束,希望能为大家提供帮助,也欢迎大家关注本站以获取更多相关内容!

作者最新文章