js的策略模式(日常笔记)

18 篇文章 0 订阅
4 篇文章 0 订阅

条条大路通罗马

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

初探策略模式

问题:绩效为 S 的人年终奖有 4 倍工资,绩效为 A 的人年终奖有 3 倍工资,而绩效为 B 的人年终奖是 2 倍工资。

代码实现:

// 定义一个方法
var calculateBonus = function(performanceLevel,salary){
    if(performanceLevel==='S'){
        return salary*4
    }
    if(performanceLevel==='A'){
        return salary*3
    }
    if(performanceLevel==='B'){
        return salary*2
    }
}
// 张三的工资
var zhangSanWages = calculateBonus('S',200000)
console.log(zhangSanWages,'zhangSanWages');

问题的深入:

  1. calculateBonus 函数比较庞大,包含了很多 if-else 语句,这些语句需要覆盖所有的逻辑 分支。
  2. calculateBonus 函数缺乏弹性,如果增加了一种新的绩效等级 C,或者想把绩效 S 的奖金
    系数改为 5,那我们必须深入 calculateBonus 函数的内部实现,这是违反开放封闭原则的。
组合函数重构代码
 var performanceLevelS=function(salary){
     return salary*4
 }
 var performanceLevelA=function(salary){
     return salary*3
 }
 var performanceLevelB=function(salary){
     return salary*2
 }
 var calculateBonus = function(performanceLevel,salary){
     if(performanceLevel==='S'){
         return performanceLevelS(salary)
     }
     if(performanceLevel==='A'){
         return performanceLevelS(salary)
     }
     if(performanceLevel==='B'){
         return performanceLevelS(salary)
     }
 }
 var zhangSanWages = calculateBonus('B',100000)
 console.log(zhangSanWages,'zhangSanWages');
使用策略模式重构

一个基于策略模式的程序至少由两部分组成。第一个部分是一组策略类,策略类封装了具体的算法,并负责具体的计算过程。第二个部分是环境类 Context,Context 接受客户的请求,随后把请求委托给某一个策略类。

策略类的实现
var performanceLevelS = function () {}
performanceLevelS.prototype.calculate = function (salary) {
    return salary * 4
}
var performanceLevelA = function () {}
performanceLevelA.prototype.calculate = function (salary) {
    return salary * 3
}
var performanceLevelB = function () {}
performanceLevelB.prototype.calculate = function (salary) {
    return salary * 2
}
var performanceLevelC = function () {}
performanceLevelC.prototype.calculate = function (salary) {
    return salary * 1
}
环境类的实现 定义奖金类 Bonus
// 环境类的实现 定义奖金类 Bonus
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 () {
    // 查看this
    console.log(this,this.strategy);
    return this.strategy.calculate(this.salary); // 把计算奖金的操作委托给对应的策略对象
}
var bonus = new Bonus();
全部代码
 // 策略类的实现
 var performanceLevelS = function () {}
 performanceLevelS.prototype.calculate = function (salary) {
     return salary * 4
 }
 var performanceLevelA = function () {}
 performanceLevelA.prototype.calculate = function (salary) {
     return salary * 3
 }
 var performanceLevelB = function () {}
 performanceLevelB.prototype.calculate = function (salary) {
     return salary * 2
 }
 var performanceLevelC = function () {}
 performanceLevelC.prototype.calculate = function (salary) {
     return salary * 1
 }
 // 环境类的实现 定义奖金类 Bonus
 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 () {
     // 查看this
     console.log(this,this.strategy);
     return this.strategy.calculate(this.salary); // 把计算奖金的操作委托给对应的策略对象
 }
 var bonus = new Bonus();
 bonus.setSalary(10000);
 bonus.setStrategy(new performanceLevelS()); // 设置策略对象
 console.log(bonus.getBonus()); // 输出:40000
js中的策略模式
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', 20000 ),'输出绩效' );
多态在策略模式中的体现

通过使用策略模式重构代码,我们消除了原程序中大片的条件分支语句。所有跟计算奖金有关的逻辑不再放在 Context 中,而是分布在各个策略对象中。Context 并没有计算奖金的能力,而是把这个职责委托给了某个策略对象。每个策略对象负责的算法已被各自封装在对象内部。当我们对这些策略对象发出“计算奖金”的请求时,它们会返回各自不同的计算结果,这正是对象多态性的体现,也是“它们可以相互替换”的目的。替换 Context 中当前保存的策略对象,便能执行不同的算法来得到我们想要的结果。

动画中的策略模式

如果让一些不太了解前端开发的程序员来投票,选出他们眼中 JavaScript 语言在 Web 开发中
的两大用途,我想结果很有可能是这样的:
 编写一些让 div 飞来飞去的动画
 验证表单

思路和一些准备工作

 动画开始时,小球所在的原始位置;
 小球移动的目标位置;
 动画开始时的准确时间点;
 小球运动持续的时间。

缓动算法
var teew = {
  // 动画已消耗的时间、小球原始位置、小球目标位置、动画持续的总时间,返回的值则是动画元素应该处在的当前位置。
  linear: function (t, b, c, d) {
      return c * t / d + b
  },
  easeIn: function (t, b, c, d) {
      return c * (t /= d) * t + b
  },
  strongEaseIn: function (t, b, c, d) {
      return c * (t /= d) * t * t * t * t + b;
  },
  strongEaseOut: function (t, b, c, d) {
      return c * ((t = t / d - 1) * t * t * t * t + 1) + b;
  },
  sineaseIn: function (t, b, c, d) {
      return c * (t /= d) * t * t + b;
  },
  sineaseOut: function (t, b, c, d) {
      return c * ((t = t / d - 1) * t * t + 1) + b;
  }
}
让我们的小球动起来

Animate类

var Animate = function (dom) {
    this.dom = dom //进行运动的节点
    this.startTime = 0
    this.startPos = 0
    this.endStart = 0
    this.propertyName = null //dom 节点需要被改变的 css 属性名
    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 = teew[easing]

    var self = this
    var timeId = setInterval(function () {
        if (self.step() === false) { // 如果动画已结束,则清除定时器
            clearInterval(timeId);
        }
    }, 19)
}
// 该方法代表小球运动的每一帧要做的事情
Animate.prototype.step = function () {
    var t = +new Date; // 取得当前时间
    if (t >= this.startTime + this.duration) { // (1) 
        this.update(this.endPos); // 更新小球的 CSS 属性值
        return false;
    }
    var pos = this.easing(t - this.startTime, this.startPos,
        this.endPos - this.startPos, this.duration);
    // pos 为小球当前位置
    this.update(pos); // 更新小球的 CSS 属性值
}
// 负责更新小球 CSS 属性值
Animate.prototype.update = function (pos) {
    this.dom.style[this.propertyName] = pos + 'px';
}
全部代码
<div style="position:absolute;background:blue" id="div">我是 div</div>
<script>
    /**
     * 实现思想
     * 动画开始小球的位置
     * 小球移动的目标位置
     * 动画开始的时间点
     * 小球移动的持续时间
     */
    var teew = {
        // 动画已消耗的时间、小球原始位置、小球目标位置、动画持续的总时间,返回的值则是动画元素应该处在的当前位置。
        linear: function (t, b, c, d) {
            return c * t / d + b
        },
        easeIn: function (t, b, c, d) {
            return c * (t /= d) * t + b
        },
        strongEaseIn: function (t, b, c, d) {
            return c * (t /= d) * t * t * t * t + b;
        },
        strongEaseOut: function (t, b, c, d) {
            return c * ((t = t / d - 1) * t * t * t * t + 1) + b;
        },
        sineaseIn: function (t, b, c, d) {
            return c * (t /= d) * t * t + b;
        },
        sineaseOut: function (t, b, c, d) {
            return c * ((t = t / d - 1) * t * t + 1) + b;
        }
    }
    var Animate = function (dom) {
        this.dom = dom //进行运动的节点
        this.startTime = 0
        this.startPos = 0
        this.endStart = 0
        this.propertyName = null //dom 节点需要被改变的 css 属性名
        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 = teew[easing]

        var self = this
        var timeId = setInterval(function () {
            if (self.step() === false) { // 如果动画已结束,则清除定时器
                clearInterval(timeId);
            }
        }, 19)
    }
    // 该方法代表小球运动的每一帧要做的事情
    Animate.prototype.step = function () {
        var t = +new Date; // 取得当前时间
        if (t >= this.startTime + this.duration) { // (1) 
            this.update(this.endPos); // 更新小球的 CSS 属性值
            return false;
        }
        var pos = this.easing(t - this.startTime, this.startPos,
            this.endPos - this.startPos, this.duration);
        // pos 为小球当前位置
        this.update(pos); // 更新小球的 CSS 属性值
    }
    // 负责更新小球 CSS 属性值
    Animate.prototype.update = function (pos) {
        this.dom.style[this.propertyName] = pos + 'px';
    }
    var div = document.getElementById('div');
    var animate = new Animate(div);
    animate.start('right', 500, 1000, 'linear');
</script>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值