js 策略模式

 js 策略模式
 例如,很多公司的年终奖是根据员工的工资基数和年底绩效情况来发放的.例如,绩效为s的人年终奖有4倍工资,
 绩效为A的人年终奖有3倍工资,而绩效为B的人年终奖是2倍工资.
 
最初思路
编写一个calculateBonus的函数来计算每个人的奖金数额.
这个函数,接受两个参数:员工的工资数额和他的绩效考核等级.
    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);
    calculateBonus('S',10000);
缺点:
calculateBonus函数比较庞大,包含了很多的if-else语句,这些语句需要覆盖所有的逻辑分支
 calculateBonus函数缺乏弹性,如果再增加一种新的绩效考核,我们必须修改函数内部实现.
 这违反了开放-封闭原则
 算法的复用性差,如果在程序的其它地方需要重用这些计算奖金的算法,我们的复制过去;
使用组合函数重构代码
一般最容易想到的办法就是使用组合函数来重构代码,我们把各种算法封装到一个个的小函数里面,
这些小函数有良好的命名,可以一目了然地知道它对应哪种算法,它们可以复用到程序的其它地方:
    var performanceS = function(salary){
        return salary * 4;
    }
    var performanceA = function(salary){
        return salary * 3;
    }
    var performanceB = function(salary){
        return salary * 2;
    }
    var calculateBons = function(performanceLevel,salary){
        if(performanceLevel === 'S'){
            return performanceS(salary)
        }
        if(performanceLevel === 'A'){
            return performanceA(salary)
        }
        if(performanceLevel === 'B'){
            return performanceB(salary)
        }
    }
    console.log(calculateBons('A',10000));
目前,虽然程序上得到一定程度上的改善,但是这种改善风格有限,问题仍然存在:
 calculateBons 函数有可能越来越大,而且在系统变化的时候缺乏弹性.


使用策略模式重构代码
策略模式指的是定义一系列的算法,把它们封装起来.目的就是将算法的使用与算法的实现分离出来
一个基于策略模式的程序至少由两部分组成.第一部分是一组策略类,封装了具体的算法,并负责具体的计算过程;
第二部分是环境类Context,Context接受用户的请求,随后把请求委托给某一个策略类.
    var performanceS = function(){};
    performanceS.prototype.calculate = function(salary){
        return salary * 4;
    }
    var performanceA = function(){};
    performanceA.prototype.calculate = function(salary){
        return salary * 3;
    }
    var performanceB = function(){};
    performanceB.prototype.calculate = function(salary){
        return salary * 2;
    }
    var 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(){// 取得奖金数额
        return this.strategy.calculate(this.salary);//把计算奖金的操作委托给对应的策略对象
    }

    var bouns = new Bonus();
    bouns.setSalary(10000);
    bouns.setStrategy(new performanceS());//设置策略对象

    console.log(bouns.getBonus());

我们让strategy对象从各个策略类中创建而来,这是模拟一些传统面向对象实现.
实际上,在js中,函数也是对象,所以更简单和直接的做法是把strategy直接定义为函数;
    var strategies = {
        'S':function(salary){
            return salary * 4;
        },
        'A':function(salary){
            return salary * 3;
        },
        'B':function(salary){
            return salary * 2;
        }
    };
    /*
    同样,Context也没有必要用Bonus类来表示,我们已然用calculateBonus函数充当Context来接受用户的请求
    * */
    var calculateBonus = function(level,salary){
        return strategies[level](salary);
    };
    console.log(calculateBonus('S',20000));


接下来,我们用策略模式重构表单验证
    var $btn = document.getElementsByTagName('button')[0],
        $registerForm = document.getElementById('registerForm');
    $btn.onclick = function(){
        if($registerForm.userName.value === ''){
            alert('用户名不能为空');
            return false;
        }
        if($registerForm.password.value.length < 6){
            alert('密码长度不能少于6位');
            return false;
        }
        if(!/(^1[3|5|8][0-9]{9}$)/.test( $registerForm.phoneNumber.value ) ){
            alert('手机号码格式不正确');
            return false;
        }
    }
这是一种常见的代码编写方式,缺点如下"
$btn.onclick函数比较庞大,包含了很多的if-else语句,这些语句覆盖了所有的校验规则
$btn.onclick函数缺乏弹性,如果增加了一种新的校验规则,或者想把密码的长度校验从6改成8,
都必须深入函数内部去修改,这是违反了开放-封闭原则;
算法的复用性差,如果在程序中增加了另外一个表单,得复制过去;
用策略模式重构表单校验
很显然第一部要把这些校验逻辑都封装成策略对象:
    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类,Validator类在这里我们作为Context,负责接受用户的请求
并委托给strategy对象.在给出Validator类的代码前,有必要提前了解用户是如何向Validator类发送请求的,
有助于我们知道如何去编写Validator类的代码
    var validataFunc = function(){
        var validator = new Validator();//创建一个validator对象
        /**添加一些校验规则**/
        validator.add(document.getElementById('userName'),'isNonEmpty','用户名不能为空');
        validator.add(document.getElementById('password'),'minLength:10','密码长度不能少于10位');
        validator.add(document.getElementById('phoneNumber'),'isMobile','手机号码格式不正确');

        var errorMsg = validator.start();//获得校验结果
        return errorMsg;
    };
    $btn.onclick = function(){
        var errorMsg = validataFunc();// 如何errorMsg有确切的返回值,说明未通过校验
        if(errorMsg){
            alert(errorMsg);
            return false;//阻止表单提交
        }
    };

可以看到,我们先创建了一个validator对象,然后通过validator.add方法,往validator对象中添加一些校验规则,
validator.add方法接受3个参数,
'minLength:6'是一个以冒号隔开的字符串;

当往validator对象里添加完一系列的校验规则之后,会调用validator.start()方法来启动校验.
如果validator.start()返回一个确切的errorMsg字符串当作返回值,说明该校验没通过,
此时阻止表单提交

最后就是Validator类的实现
    var Validator = function(){
        this.cache = []; //保存校验规则
    };
    Validator.prototype.add = function( dom, rule, errorMsg ) {
        var ary = rule.split(':'); //把strategy和参数分开
        this.cache.push(function () { //把校验的步骤用空函数包装起来,并且放入cache
            var strategy = ary.shift();// 用户挑选的strategy
            ary.unshift(dom.value);//把input的value添加进参数列表
            ary.push(errorMsg); //把errorMsg添加参数列表
            return strategies[strategy].apply(dom, ary);
        });
    };

    Validator.prototype.start = function(){
        console.log(this.cache);
        for ( var i = 0, validatorFunc; validatorFunc = this.cache[ i++ ]; ){
            var msg = validatorFunc();// 开始校验,并取得校验后的返回信息
            if ( msg ){// 如果有确切的返回值,说明校验没有通过;
                return msg;
            }
        }
    };







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值