一. 认识策略模式
策略模式的定义:定义一系列的算法,将他们一个个封装起来,使他们直接可以相互替换。
策略模式是开发中常用的第二种设计模式,它在开发中非常常见,由两部分组成。第一部分是策略类,封装了许多具体的,相似的算法。第二部分是环境类,接受客户请求,随后将请求委托给策略类。说的通俗一点就是将相同算法的函数存放在一个包装里边,每个函数用相同的方式拿出来,就叫做策略模式。下面我们来通过代码实现深入了解一下。
二. 具体实现和思想
假如需要实现一个计算员工奖金的程序,效绩为 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 分支,减少了性能消耗,并且使代码有了弹性,随时增加其他绩效,不需要更改原代码。
主要思想:这段代码基于面向对象语言,引入了多态的概念,不适用于js。
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
分析:js 的对象可以直接创建,将函数封装进去,这样一来,代码显得清晰简洁。代码的复用,弹性也随之变强。
以上就是 js 设计模式策略模式的主要思想和实现,他在应用中有两个主要的作用,一是策略模式实现晃动动画;二是实现表单验证,有能力有兴趣的小伙伴可以往下看。
三. 策略模式的实际运用
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 中,使他们易于切换、扩展。
本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注编程网的更多内容!