最近在看《JavaScript设计模式与开发实践》这本书,受益匪浅,小记录一下书中的各个demo,加深理解;

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

案例1:很多公司的年终奖是根据员工的工资基数和年底绩效情况来发放的。例如,绩效为S 的人年终奖有4 倍工资,绩效为A 的人年终奖有3 倍工资,而绩效为B 的人年终奖是2 倍工资。假设财务部要求我们提供一段代码,来方便他们计算员工的年终奖。用JS实现这个很简单html 代码

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); // 输出:40000
calculateBonus('S', 6000); // 输出:24000

这个代码是非常简单的,但是缺点很明显:

a、calculateBonus 函数比较庞大,包含了很多if-else 语句,这些语句需要覆盖所有的逻辑分支。

b、calculateBonus 函数缺乏弹性,如果增加了一种新的绩效等级C,或者想把绩效S 的奖金系数改为5,那我们必须深入calculateBonus 函数的内部实现,这是违反开放封闭原则的。

c、算法的复用性差,如果在程序的其他地方需要重用这些计算奖金的算法呢?我们的选择只有复制和粘贴。

这个时候策略模式就用上场了;见代码:

html 代码

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)); // 输出:80000
console.log(calculateBonus('A', 10000)); // 输出:30000

案例二:实现一个js当中运动的DIV;

实用策略模式实现如下:

html 代码

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>javascript策略模式的应用!</title>
</head>
<body>
<div style="position:absolute;background:blue; width: 100px;height: 100px;color: #fff;" id="div">我是div</div>
<script> var tween = {
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; // 进行运动的dom 节点
this.startTime = 0; // 动画开始时间
this.startPos = 0; // 动画开始时,dom 节点的位置,即dom 的初始位置
this.endPos = 0; // 动画结束时,dom 节点的位置,即dom 的目标位置
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; // dom 节点需要被改变的CSS 属性名
this.endPos = endPos; // dom 节点目标位置
this.duration = duration; // 动画持续事件
this.easing = tween[easing]; // 缓动算法
var self = this;
var timeId = setInterval(function() { // 启动定时器,开始执行动画
if (self.stop() === false) { // 如果动画已结束,则清除定时器
clearInterval(timeId);
}
}, 19);
};
Animate.prototype.stop = 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 属性值
};
Animate.prototype.update = 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'); </script>
</body>
</html>

案列三:实现表单验证;

html 代码

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title></title>
<link rel="stylesheet" href="">
</head>
<body>
<form action="http:// xxx.com/register" id="registerForm" method="post">
请输入用户名:<input type="text" name="userName"/ ><br>
请输入密码:<input type="text" name="password"/ ><br> 请输入手机号码:<input type="text" name="phoneNumber"/ ><br>
<button>提交</button><br>
</form>
<script>
var registerForm = document.getElementById('registerForm');
registerForm.onsubmit = 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;
}
}
</script>
</body>
</html>

这是一种很常见的代码编写方式,它的缺点跟计算奖金的最初版本一模一样。因此,还是要使用策略模式; 代码如下:

html 代码

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title></title>
<link rel="stylesheet" href="">
</head>
<body>
<form action="http:// xxx.com/register" id="registerForm" method="post">
请输入用户名:<input type="text" name="userName"/ ><br>
请输入密码:<input type="text" name="password"/ ><br> 请输入手机号码:<input type="text" name="phoneNumber"/ ><br>
<button>提交</button><br>
</form>
<script> /***********************策略对象**************************/
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 类**************************/
var Validator = function() {
this.cache = [];
};
Validator.prototype.add = function(dom, rules) {
var self = this;
for (var i = 0, rule; rule = rules[i++];) {
(function(rule) {
var strategyAry = rule.strategy.split(':');
var errorMsg = rule.errorMsg;
self.cache.push(function() {
var strategy = strategyAry.shift();
strategyAry.unshift(dom.value);
strategyAry.push(errorMsg);
return strategies[strategy].apply(dom, strategyAry);
});
})(rule)
}
};
Validator.prototype.start = function() {
for (var i = 0, validatorFunc; validatorFunc = this.cache[i++];) {
var errorMsg = validatorFunc();
if (errorMsg) {
return errorMsg;
}
}
};
/***********************客户调用代码**************************/
var registerForm = document.getElementById('registerForm');
var validataFunc = function() {
var validator = new Validator();
validator.add(registerForm.userName, [{
strategy: 'isNonEmpty',
errorMsg: '用户名不能为空'
}, {
strategy: 'minLength:6',
errorMsg: '用户名长度不能小于10 位'
}]);
validator.add(registerForm.password, [{
strategy: 'minLength:6',
errorMsg: '密码长度不能小于6 位'
}]);
validator.add(registerForm.phoneNumber, [{
strategy: 'isMobile',
errorMsg: '手机号码格式不正确'
}]);
var errorMsg = validator.start();
return errorMsg;
}
registerForm.onsubmit = function() {
var errorMsg = validataFunc();
if (errorMsg) {
alert(errorMsg);
return false;
}
};
</script>
</body>
</html>

策略模式是一种常用且有效的设计模式,本章提供了计算奖金、缓动动画、表单校验这三个例子来加深大家对策略模式的理解。从这三个例子中,我们可以总结出策略模式的一些优点。

a、策略模式利用组合、委托和多态等技术和思想,可以有效地避免多重条件选择语句。

b、策略模式提供了对开放—封闭原则的完美支持,将算法封装在独立的strategy 中,使得它们易于切换,易于理解,易于扩展。

c、策略模式中的算法也可以复用在系统的其他地方,从而避免许多重复的复制粘贴工作。

当然,策略模式也有一些缺点,但这些缺点并不严重。可略过。。

最新文章

  1. 【云知道】LoadRunner 录制问题集锦
  2. nfs配置注意点
  3. squid日志时间转换
  4. EJB3Persistence开发手册-原生SQL查询(NativeSQL)
  5. cdoj 1246 每周一题 拆拆拆~ 分解质因数
  6. 【iOS与EV3混合机器人编程一系列五个】iOS_WiFi_EV3_Library 解剖连接EV3
  7. Longest Palindromic Substring -LeetCode
  8. canvas总结:线段宽度与像素边界
  9. Syntax error on tokens, delete these tokens.问题解决
  10. cURL模拟网页登陆
  11. 【SSH系列】Hibernate映射 -- 多对多关联映射
  12. A Java Runtime Environment (JRE) or Java Development Kit (JDK) must be available in order to run Eclipse. No Java virtual machine was found after searching the following locations: /usr/local/eclipse/
  13. 【译】如何使用Vue捕获网络摄像头视频
  14. SQL SERVER 批量生成编号
  15. 学习js第三天小结
  16. HDU 1556 区间查询
  17. chrome插件的popup与跨域请求
  18. C语言第一个例子hello world
  19. 【BZOJ1068】压缩(动态规划)
  20. word调整技巧

热门文章

  1. 【转】harbor仓库高可用集群部署说明
  2. nginx的跨域设置
  3. MongoDB之安装部署
  4. sftp远程传输脚本
  5. Layui + 微信小程序 + PC端 + GatewayWorker + 移动端即时聊天系统
  6. hdu2476(区间dp+dp)
  7. 2018-2019-2 《网络对抗技术》 Exp6 信息搜集与漏洞扫描 20165222
  8. 性能监控 | MAT分析内存泄漏
  9. OpenJudge计算概论-买房子
  10. 设备树中指定的中断触发方式与request_irq中指定的触发方式不一致时,内核会使用哪种中断触发方式呢?