Javascript设计模式与开发实践详解(二:策略模式) http://www.jianshu.com/p/ef53781f6ef2

上一章我们介绍了单例模式及JavaScript惰性单例模式应用
这一次我主要介绍策略模式
策略模式是定义一系列的算法,把它们一个个封装起来,并且让他们可以互相替换。
比方说在现实中很多时候也有很多途径到达同一个目的地,比如我们去某个地方旅游,可以选择坐飞机,乘火车,骑自行车等方式。

使用策略模式计算奖金

很多公司的年终奖是根据员工的工资基数和年底绩效来发放的。例如,绩效为 S 的人年终奖有4倍工资,绩效为 A 的人年终奖有3倍工资,绩效为 B 的人年终奖有2倍工资。
现在我们来计算员工的年终奖。

  var performanceS = function  (salary) {
return salary * 4;
}
var performanceA = function  (salary) {
return salary * 3;
}
var performanceB = function  (salary) {
return salary * 2;
}
var calculateBonus = function  (performanceLevel,salary) {
if(performanceLevel === ‘S‘)
    return performanceS(salary)
if(performanceLevel === ‘A‘)
    return performanceA(salary)
if(performanceLevel === ‘B‘)
    return performanceB(salary)

}
calculateBonus(‘A‘,10000);  // 30000

值得一提的是以上代码并不符合策略模式定义
策略模式是定义一系列的算法,把它们一个个封装起来,并且让他们可以互相替换。
这句话理解起来就是定义一系列的算法,把它们各自封装成策略类,算法被封装在策略类内部的方法里。
因此我们必须做点改变

在JavaScript中,函数也是对象,所以我们可以直接将定义一个对象strategy用来存放策略类。
参见代码:

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

以上基本符合了策略模式的定义及写法。

更广义的算法

用策略模式仅仅来封装算法未免大材小用,在实际开发中,也可以用策略模式来封装一些业务规则。
接下来我们用策略模式来完成表单验证的例子。

表单验证

校验表单信息想必都是稀松平常的事情,假设我们正在编写一个注册的页面,在点击注册按钮之前,有如下几条校验逻辑。

1.用户名不能为空
2.密码长度不少于6位
3.手机号码必须符合格式

假设我们现在没有引入策略模式,那么普通的写法是这样的:

<body>
 <form action=‘xxx‘ id=‘registerForm‘ method=‘post‘>
      please entry username : <input type=‘text‘ name=‘username‘ />
      please entry password : <input type=‘text‘ name=‘password‘ />
      please entry phoneNumber : <input type=‘text‘ name=‘phoneNumber‘ />
             <button>submit</button>
 </form>
 <script>
     var registerForm = document.getElementById(‘registerForm‘)
     registerForm.onsubmit = function  () {
         if(registerForm.username.value == ‘‘){
              alert(‘username cannot be none‘)
              return false;
         }
         if(registerForm.password.value.length < 6 ){
              alert(‘password length cannot less than 6‘)
              return false;
         }
         if(!/(^1[3][5][8][0-9]{9}$)/.test(registerForm.phoneNumber.value)){
             alert(‘phoneNumber format error‘)
             return false;
         }
     }
 </script>
</body>

可想而知这种面向过程的方法繁琐且不复用缺乏弹性,总而言之,我们必须用策略模式重构之。

注意我们的核心思想就是要写两个类: 一个策略类 一个内容类

用策略模式重构表单校验

第一步我们要把这些校验逻辑封装成策略对象,也就是策略类:

var strategies = {
 isNonEmpty: function  (value ,errorMsg) {
     if(value == ‘‘) return errorMsg
 },
 minLength: function  (value,length,errorMsg) {
     if(value.length < length) return errorMsg
 },
inMobile: function  (value,errorMsg) {
    if(!/(^1[3][5][8][0-9]{9}$)/.test(value)) return errorMsg
}
};

ok ,现在我们开始写内容类。Validator 类在这里作为context ,负责接收用户的请求,并委托给strategy对象。

var Validator = function  () {
this.cache = [];   // save the check format
}
Validator.prototype.add = function  (dom,rule,errorMsg) {
var ary = rule.split(‘:‘)      // forExample : ‘minLength:10‘
this.cache.push(function  () {
    var strategy = ary.shift() //  minLength
    ary.unshift(dom.value)     // forExample : registerForm.username
    ary.push(errorMsg);        //  ary= [registerForm.username,10]
     return strategies[strategy].apply(dom,ary)    //bring the dom & ary into strategies props.

});
};
Validator.prototype.start = function  () {
for (var i = 0; ValidatorFunc; ValidatorFunc = this.cache[i++]) {
     var msg = ValidatorFunc();
     if(msg) return msg;  // if have return value then the check is failed.
};
}

如果我们没有写如何用户如何向Validator类发送请求,上面理解起来可能比较复杂。现在我们具体到表单看看。

var ValidatorFunc = function  () {
var validator = new Validator()
/***************add some format by the prototype     fun.*************************/
   validator.add(registerForm.username,‘isNonEmpty‘,‘username cannot be     none‘);
   validator.add(registerForm.password,‘minLength:6‘,‘password cannot be none‘);
   validator.add(registerForm.phoneNumber,‘isMoblie‘,‘phoneNumber cannot be none‘);

   var errorMsg = Validator.start() // get the check result
   return errorMsg                  // return the result. if exsit ...
}

  var registerForm = document.getElementById(‘registerForm‘)
  registerForm.onsubmit = function  () {
  var errorMsg = validatorFunc()   // this is a instance!!!
  if(errorMsg)
      alert(errorMsg)
  return false
}

这样我们大致完成了一个表单的策略模式重构(如果你能马上立刻看懂的话...)

值得一提的是我们现在可以非常轻松简单的修改校验规则,例如

validator.add(registerForm.username,‘minLength:10‘,‘username cannot less than 10‘)

小结

策略模式是一种常用且有效的设计模式,我们可以总结出策略模式的一些优点。

  1. 有效避免多重条件选择语句
  2. 完美支持了开发--封闭原则
  3. 易于复用
时间: 2025-01-05 10:29:53

Javascript设计模式与开发实践详解(二:策略模式) http://www.jianshu.com/p/ef53781f6ef2的相关文章

JavaScript设计模式与开发实践---读书笔记(5) 策略模式

策略模式的定义是:定义一系列的算法,把它们一个个封转起来,并且使它们可以相互替换. JavaScript版本的策略模式: 奖金系统: var strategies = { "S": function(salary){ return salary*4; }, "A": function(salary){ return salary*3; }, "B": function(salary){ return salary*2; } }; var calc

JavaScript设计模式与开发实践随笔(二)

多态 多态的实际含义是:同一操作作用于不同的对象上面,可以产生不同的解释和不同的执行结果.换句话说,给不同的对象发送同一个消息的时候,这些对象会根据这个消息分别给出不同的反馈 var makeSound = function( animal ){ if ( animal instanceof Duck ){ console.log( '嘎嘎嘎' ); }else if ( animal instanceof Chicken ){ console.log( '咯咯咯' ); } }; var Du

JavaScript设计模式与开发实践---读书笔记(7) 迭代器模式

迭代器模式是指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示. JavaScript中的Array.prototype.foreach. 1.JQuery中的迭代器 $.each函数 2.自己实现一个each函数 var each = function(ary,callback){ for(var i=0,l=ary.length;i<l;i++){ callback.call(ary[i],i,ary[i]);//把下标和元素当作参数传给callback函数 }

JavaScript设计模式与开发实践---读书笔记(9) 命令模式

命令模式的用途: 命令模式是最简单和优雅的模式之一,命令模式中的命令(command)指的是一个执行某些特定事情的指令. 命令模式最常见的应用场景是:有时候需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是什么.此时希望用一种松耦合的方式来设计程序,使得请求发送者和请求接收者能够消除彼此之间的耦合关系. 命令模式的例子-菜单程序: <!DOCTYPE html> <html lang="en"> <head> <met

JavaScript设计模式与开发实践---读书笔记(10) 组合模式

组合模式就是用小的子对象来构建更大的对象,而这些小的子对象也许是由更小的"孙对象"构成的. 组合模式将对象组合成树形结构,以表示"部分-整体"的层次结构. 抽象类在组合模式中的作用: 组合模式最大的优点在于可以一致地对待组合对象和基本对象.这种透明性带来的便利,在静态类型语言中体现的尤为明显. JavaScript中实现组合模式的难点在于要保证组合对象和叶对象拥有同样的方法,这通常需要用鸭子类型的思想对它们进行接口检查. 透明性带来的安全问题: 组合模式的例子-扫描

《JavaScript设计模式与开发实践》读书笔记之观察者模式

1.<JavaScript设计模式与开发实践>读书笔记之观察者模式 观察者模式定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知. JavaScript中通常采用事件模型替代传统的观察者模式 1.1 逐步实现观察者模式 以客户看房为例 首先指定谁充当发布者,如售楼处 然后给发布者添加一个缓存列表,用于存放回调函数以便通知订阅者.这里为了让订阅者只接收自己感兴趣的消息,增加一个标识key 最后发布消息时候,发布者遍历缓存列表,依次触发里面存放的订阅者的回

JavaScript 设计模式与开发实践读书笔记 http://www.open-open.com/lib/view/open1469154727495.html

JavaScript 设计模式与开发实践读书笔记 最近利用碎片时间在 Kindle 上面阅读<JavaScript 设计模式与开发实践读书>这本书,刚开始阅读前两章内容,和大家分享下我觉得可以在项目中用的上的一些笔记. 我的 github 项目会不定时更新,有需要的同学可以移步到我的 github 中去查看源码: https://github.com/lichenbuliren/design-mode-notes 1.currying 函数柯里化 currying 又称 部分求值 .一个 cu

JavaScript设计模式与开发实践 – 观察者模式 http://web.jobbole.com/87809/

概述 观察者模式又叫发布 – 订阅模式(Publish/Subscribe),它定义了一种一对多的关系,让多个观察者对象同时监听某一个目标对象(为了方便理解,以下将观察者对象叫做订阅者,将目标对象叫做发布者).发布者的状态发生变化时就会通知所有的订阅者,使得它们能够自动更新自己. 观察者模式的使用场合就是:当一个对象的改变需要同时改变其它对象,并且它不知道具体有多少对象需要改变的时候,就应该考虑使用观察者模式. 观察者模式的中心思想就是促进松散耦合,一为时间上的解耦,二为对象之间的解耦.让耦合的

【读书】JavaScript 设计模式与开发实践

2016.08.30 <JavaScript 设计模式与开发实践> 曾探 人民邮电出版社 2016年5月第1版 p13 找到变化的部分并封装之,以使得容易替换:而剩下的就是不变的部分. P49 函数柯里化(currying)的作用是多次收集参数,然后作为数组传给处理函数再一次执行. 其意义在于预处理--将预处理的流程放到一个函数里会更为清晰可控. P57 惰性加载函数 在函数内部重写引用函数的外部变量的引用,从而在第一次"调用"此变量后,此变量就指向新的正确的函数. p84