美文网首页前端进阶与开发实践Web前端之路
翻滚吧,设计模式之02策略模式笔记

翻滚吧,设计模式之02策略模式笔记

作者: 莫闻 | 来源:发表于2017-05-12 23:17 被阅读15次
  • 策略模式的定义是: 定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。

问题提出:

  • 很多公司的年终奖是根据员工的工资基数和年底绩效情况来发放的。例如,绩效为 S 的人年终奖有 4 倍工资,绩效为 A 的人年终奖有 3 倍工资而绩效为 B 的人年终奖是 2 倍工资。假设财务部要求我们提供一段代码,来方便他们计算员工的年终奖。
  1. 最初的代码实现
var calculateBonus = function(level, salary){
      if(level === "s"){
          return salary*4;    
      }else if(level === "a"){
          return salary*3; 
      }else if(level === "b"){
          return salary*2; 
      }
}
console.log(calculateBonus(10000,"a"));//30000
console.log(calculateBonus(10000,"b"));//20000
  • 可以看出上面的代码有着很多缺点:
    • 函数包含太多if else 语句
    • 函数缺乏弹性,如果需要添加或者减小一个等级,就必须更改calculateBonus函数的代码
    • 复用性差,只能来复制粘贴
//可以使用组合函数进行代码重构
var levelS = function(salary){
    return salary*4;
}
var levelA = function(salary){
    return salary*3;
}
var levelB = function(salary){
    return salary*2;
}

var calculateBonus = function(level, salary){
    if(level == "s"){
        return levelS(salary);
    }else if(level == "a"){
        return levelA(salary);
    }if(level == "b"){
        return levelB(salary);
    }
}
//我们可以看到,将具体的方法进行了分离
//但是这种重构方法的作用感觉很有限,没有解决调本质的问题

  • 使用策略模式重构代码
  • 策略模式指的是:定义一系列的算法,将他们一个个封装起来。将不变的部分和变化的部分隔离开来是每个设计模式的主题。
  • 策略模式的目的就是将算法的使用和算法的实现隔离开来。
  • 在本例中算法的使用方式是不变的,都是根据某个算法计算得到奖金值,而算法计算奖金的方式却是不同的,每种绩效对应不同的计算规则。
  • 一个基于策略模式的程序至少包含两部分:
  1. 策略类:封装了具体的算法,并负责集体的计算过程。
  2. 环境类:context 接受客户请求,随后吧请求委托给某一个策略类,说明了context中要维持对某个策略对象的引用;

先看一下js模仿传统的面向对象的实现方法

// 策略类
var performaceS = function(){

}
performaceS.prototype.calculate = function(salary){
   return salary*4;
}

var performaceA = function(){

}
performaceA.prototype.calculate = function(salary){
   return salary*4;
}

var performaceB = function(){

}
performaceB.prototype.calculate = function(salary){
   return salary*4;
}


//- 定义奖金了类
var Bouns = 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);/把计算奖金的操作委托给对应策略对象
}
//让我们再来回顾下策略模式的思想:定义一系列的算法,把他们一个个封装起来,并且可以使他们相互替换。
//详细点就是:定义一系列算法,把他们封装成策略类,算法被封装到策略类内部的方法里。
//客户对Context发起请求时,context总是把请求委托给这些策略对象中的某一个进行计算。

var bonus = new Bonus();
bonus.setSalary(10000);
bonus.setStrategy(new perfomaceS());//设置策略类对象
console.log(bonus.getBonus());

bonus.setSalary(20000);
bonus.setStrategy(new performaceA());//设置策略类
console.log(bonus.getBonus());

策略模式js版的正式实现

  • 由于在js中函数也是对象
//策略类(对象)
var strategies = {
    "S":function(salary){
        return salary * 4;
    },
    "A":function(salary){
        return salary * 3;
    },
    "B":function(salary){
        return salary * 2;
    }
}

//Context
var calculateBonus = function(level, salary) {
    return strategies[level](salary);
}
console.log(calculateBonus("S",10000));
//可以看出来将构造函数进行简化,直接采用对象,简化了代码的体积,消除了原来在代码中的大片if语句,如果后期我们有其他需求时(添加一个C类),只需要修改策略类就行了

再来举一个web中很常见的例子,表单提交时我们要进行验证

  • 比如验证姓名是否填写;

  • 密码强度

  • 手机号码等

  • 先看下最常见的代码实现

//html
请输入用户名: <input type="text" name="userName"/ >
请输入密码: <input type="text" name="password"/ >
请输入手机号码: <input type="text" name="phoneNumber"/ >
<button id="btn">提交</button>
//js
var btn = document.getElementById("btn");
//伪代码
btn.onclick = function(){
    if(userName.value === ""){//
        alert("用户名不能为空");
        return false;
    }
    if(password === rexExp){
        alert();
        return false;
    }
    if(phoneNumber === regExp1){
        alert();
        return false;
    }
}
  • 用策略模式重构
//策略类,封装算法
var strategies = {
    isEmpty:function(value, errMsg){
        if(value.trim() == ''){
            return errMsg
        }
    },
    minLength:function(value, length, errMsg){
        if(value.length < length){
            return errMsg;
        }
    },
    isMobile:function(value,errMsg){
        if(!/(^1[3|5|8][0-9]{9}$)/.test( value ) ){
            return errMsg;
        }
    }
};

var Validator = function(){
    this.cache = [];
}

Validator.prototype.add = function(dom, rule, errMsg) {
    var arr = rule.split(":");
    //r比如数据参数是这样的:egisterForm.userName, 'isNonEmpty', ' 用户名不能为空'
    this.cache.push(function(){//把校验的步骤用空函数包裹起来
        var strategy = arr.shift();//用户挑选的strategy
        arr.push(dom.value);//将input的值传入缓存数组中
        arr.push(errMsg);//将error也添加进数组中
        return strategies[strategy].apply(dom, arr);
    });
}
Validator.prototype.start = function(){
    for(var i = 0,ValidatorFun; ValidatorFun =  this.cache[ i++ ]){
        var msg = ValidatorFun();//开始校验并返回数据
        if(msg){//如果有返回说明没有通过
             return msg;
        }
    }
}

var form = document.getElementById( 'registerForm' );
var validataFunc = function(){
   var validator = new Validator();
   validator.add(form.userName, 'isEmpty', '对象不能为空');
   validator.add( form.password, 'minLength:6', ' 密码长度不能少于 6 位' );
   var errMsg = volidator.start();
    return errMsg;
}
form.onsubmit = function(){
     var errorMsg = validataFunc(); // 如果 errorMsg 有确切的返回值,说明  未通过校验
     if ( errorMsg ){
       alert ( errorMsg );
       return false; // 阻止表单提交
     }
};

参考自javascript设计模式与开发实践

相关文章

网友评论

    本文标题:翻滚吧,设计模式之02策略模式笔记

    本文链接:https://www.haomeiwen.com/subject/dvcatxtx.html