资料整理:JavaScript设计模式与开发实践
策略模式:定义一系列算法,并把它们一个一个封装起来,并且使它们可以相互替换。目的就是将算法的使用与算法的实现分离开来。
1.使用策略模式计算奖金。
一个基于策略模式的程序至少有两部分构成,第一部分是一组策略类,封装了具体的算法,并负责具体的计算过程,另一组是环境类Context,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(strategy) {
return this.strategy.calculate(this.salary);
};
var bonus = new Bonus();
bonus.setSalary(1000);
bonus.setStrategy(new PerformanceS());
bonus.getBonus();
//调用bonus.getBonus()计算时,bonus对象本身并没有能力进行计算,而是把请求委托给了之前保存好的策略对象
JavaScript版本的策略模式
var strategys = {
S: function(salary) {
return salary * 4;
},
A: function(salary) {
return salary * 3;
},
B: function(salary) {
return salary * 2;
}
};
var calculate = function(level, salary) {
return strategys[level](salary);
}
var bonus = calculate('S',1000);
多态在策略模式中的体现:通过策略模式重构代码,消灭了大量的if else条件分支语句。所有计算逻辑不在放在Context中,而是分布在各个策略对象中,Context本身没有计算的能力,而是把这个职责委托给了某个策略对象。每个策略对象负责的算法被各自封装在对象内部,当我们对这些策略对象发出运算请求时,他们会返回不同的运算结果,这正是多态性的体现,也是“它们可以相互替换”的目的。
5.4 使用策略模式实现缓动动画
tips: 在经典的动画中,缓慢开始然后加速称为“缓入”(ease in),开始很快然后减速称为“缓出”(ease out),有时候两者结合叫做“缓入缓出”(ease in out)。缓动可以使动画不再那么生硬。没有缓动的动画称为线性动画。
//t:已花费时间,o:原始位置,g:目标位置,d:持续总时间, 返回值为当前应处的位置
var tweening = {
linear: function(t, o, g, d) {
return g * t / d + o;
},
easeIn: function(t, o, g, d) {
return g * ( t /= d ) * t + o;
},
strongEaseIn: function(t, o, g, d) {
return g * ( t /= d ) * t * t * t * t + o;
},
strongEaseOut: function(t, o, g, d) {
return g * ( ( t = t / d - 1) * t * t * t * t + 1 ) + o;
},
sineaseIn: function(t, o, g, d) {
return g * ( t /= d) * t * t + o;
},
sineaseOut: function(t, o, g, d) {
return g * ( ( t = t / d - 1) * t * t + 1 ) + o;
},
}
var Animate = function(dom) {
this.dom = dom; //dom节点
this.startTime = 0;
this.originPos = 0;
this.goalPos = 0;
this.duration = 0;
this.easing = null;
this.propertyName = null;
}
Animate.prototype.start = function(propertyName, goalPos, duration, easing) {
this.startTime = +new Date;
//+将元素转换为Number类型,等同于new Date().valueOf() 或new Date().getTime(),getTime方法比+号转换性能耗费少很多
this.originPos = this.dom.getBoundingClientRect()[propertyName];// getBoundingClientRect()方法返回元素的大小及其相对于视口的位置。
this.duration = duration;
this.goalPos = goalPos;
this.propertyName = propertyName;
this.easing = tweening[easing];
var self = this;
var timeId = setInterval(function() {
if (self.step() === false) {
clearInterval(timeId);
}
}, 19);
};
Animate.prototype.step = function() {
var t = new Date().valueOf();
if (t >= this.startTime + this.duration) {
this.updatePos(this.goalPos);
return false;
}
var pos = this.easing(t - this.startTime, this.originPos, this.goalPos - this.originPos, this.duration);
this.updatePos(pos);
};
Animate.prototype.updatePos = function(pos) {
this.dom.style[this.propertyName] = pos + 'px';
}
var div = document.getElementById('div');
var animate = new Animate(div);
animate.start( 'left', 500, 1000, 'strongEaseOut' );
策略模式实现并不复杂,关键是从策略模式实现的背后,找到封装变化,委托,和多态性这些思想的价值。
5.6用策略模式实现表单校验
var strategies = {
isNotEmpty: function(value, errMsg) {
if (value === '') 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 ) ) {
// /^abc/代表开始,$代表结束,[^abc]代表求反值
return errMsg;
}
}
};
var Validator = function() {
this.cache = [];
};
Validator.prototype.add = function(value, rule, errMsg) {
var ary = rule.split(':');
this.cache.push(function() { //将校验的步骤用空函数包起来,并塞进cache
var strategy = ary.shift();
ary.unshift(value);
ary.push(errMsg);
console.log(ary);
return strategies[strategy].apply(null, ary);
});
}
Validator.prototype.start = function() {
for (let i = 0, validatorFunc; validatorFunc = this.cache[i++];) {
const msg = validatorFunc();
if (msg) {
return msg;
}
}
}
var validatorFun = function() {
var validator = new Validator();
validator.add('123','isNotEmpty', '不能为空');
validator.add(4562367,'minLength:6', '最小长度为6');
validator.add(12344567789,'isMobile', '请传入正确的手机号格式');
var errMsg = validator.start();
return errMsg;
}
var msg = validatorFun();
if (msg) {
console.log(msg);
}
给同一个值添加多种校验规则
var strategies = {
isNotEmpty: function(value, errMsg) {
if (value === '') 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 ) ) {
// /^abc/代表开始,$代表结束,[^abc]代表求反值
return errMsg;
}
}
};
var Validator = function() {
this.cache = [];
};
Validator.prototype.add = function(value, rules) {
var self = this;
for (let i = 0, rule; rule = rules[i ++];) {
(function() {
var ary = rule.strategy.split(':');
var errMsg = rule.errMsg;
self.cache.push(function() { //将校验的步骤用空函数包起来,并塞进cache
var strategy = ary.shift();
ary.unshift(value);
ary.push(errMsg);
return strategies[strategy].apply(null, ary);
});
})(rule); //用闭包将变量保存起来
}
}
Validator.prototype.start = function() {
for (let i = 0, validatorFunc; validatorFunc = this.cache[i++];) {
const msg = validatorFunc();
if (msg) {
return msg;
}
}
}
var validatorFun = function() {
var validator = new Validator();
validator.add(12344567789,[{strategy:'isMobile', errMsg:'请传入正确的手机号格式'},{strategy:'minLength:6',errMsg:'最少六个字'}]);
var errMsg = validator.start();
return errMsg;
}
var msg = validatorFun();
if (msg) {
console.log(msg);
}
5.7策略模式优缺点
优点:
- 策略模式通过运用组合,委托和多态等技术和思想,可以有效的避免多重条件选择语句。
- 策略模式提供了对开发-封闭原则的完美支持,将算法封装在独立的strategy中,使得他们易于切换,易于扩展,易于理解。
- 算法可以在其他地方复用,减少代码重复性。
- 利用委托组合让Context具有执行算法的能力,这也是继承的更轻便的一种替代方案。
小缺点:
- 使用策略模式会在程序中增加许多策略类或策略对象,但这比把它们负责的逻辑堆砌在Context中要好。
- 要使用策略模式,必须了解所有strategy,了解所有策略的不同点,才能找到一个合适的strategy,此时strategy要向客户暴露它的所有实现,这是违反最少知识原则的。
网友评论