javascript设计模式之策略模式学习笔记

1. 理解javascript中的策略模式

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

使用策略模式的优点如下:

优点:

1. 策略模式利用组合,委托等技术和思想,有效的避免很多if条件语句。

2. 策略模式提供了开放-封闭原则,使代码更容易理解和扩展。

3. 策略模式中的代码可以复用。

一:使用策略模式计算奖金;

下面的demo是我在书上看到的,但是没有关系,我们只是来理解下策略模式的使用而已,我们可以使用策略模式来计算奖金问题;

比如公司的年终奖是根据员工的工资和绩效来考核的,绩效为A的人,年终奖为工资的4倍,绩效为B的人,年终奖为工资的3倍,绩效为C的人,年终奖为工资的2倍;现在我们使用一般的编码方式会如下这样编写代码:

var calculateBouns = function(salary,level) {
  if(level === 'A') {
    return salary * 4;
  }
  if(level === 'B') {
    return salary * 3;
  }
  if(level === 'C') {
    return salary * 2;
  }
};
// 调用如下:
console.log(calculateBouns(4000,'A')); // 16000
console.log(calculateBouns(2500,'B')); // 7500

第一个参数为薪资,第二个参数为等级;

代码缺点如下:

calculateBouns 函数包含了很多if-else语句。

calculateBouns 函数缺乏弹性,假如还有D等级的话,那么我们需要在calculateBouns 函数内添加判断等级D的if语句;

算法复用性差,如果在其他的地方也有类似这样的算法的话,但是规则不一样,我们这些代码不能通用。

2. 使用组合函数重构代码

组合函数是把各种算法封装到一个个的小函数里面,比如等级A的话,封装一个小函数,等级为B的话,也封装一个小函数,以此类推;如下代码:

var performanceA = function(salary) {
  return salary * 4;
};
var performanceB = function(salary) {
  return salary * 3;
};

var performanceC = function(salary) {
  return salary * 2
};
var calculateBouns = function(level,salary) {
  if(level === 'A') {
    return performanceA(salary);
  }
  if(level === 'B') {
    return performanceB(salary);
  }
  if(level === 'C') {
    return performanceC(salary);
  }
};
// 调用如下
console.log(calculateBouns('A',4500)); // 18000

代码看起来有点改善,但是还是有如下缺点:

calculateBouns 函数有可能会越来越大,比如增加D等级的时候,而且缺乏弹性。

3. 使用策略模式重构代码

策略模式指的是 定义一系列的算法,把它们一个个封装起来,将不变的部分和变化的部分隔开,实际就是将算法的使用和实现分离出来;算法的使用方式是不变的,都是根据某个算法取得计算后的奖金数,而算法的实现是根据绩效对应不同的绩效规则;

一个基于策略模式的程序至少由2部分组成,第一个部分是一组策略类,策略类封装了具体的算法,并负责具体的计算过程。第二个部分是环境类Context,该Context接收客户端的请求,随后把请求委托给某一个策略类。我们先使用传统面向对象来实现;

如下代码:

var performanceA = function(){};
performanceA.prototype.calculate = function(salary) {
  return salary * 4;
};
var performanceB = function(){};
performanceB.prototype.calculate = function(salary) {
  return salary * 3;
};
var performanceC = function(){};
performanceC.prototype.calculate = function(salary) {
  return salary * 2;
};
// 奖金类
var Bouns = function(){
  this.salary = null;  // 原始工资
  this.levelObj = null; // 绩效等级对应的策略对象
};
Bouns.prototype.setSalary = function(salary) {
  this.salary = salary; // 保存员工的原始工资
};
Bouns.prototype.setlevelObj = function(levelObj){
  this.levelObj = levelObj; // 设置员工绩效等级对应的策略对象
};
// 取得奖金数
Bouns.prototype.getBouns = function(){
  // 把计算奖金的操作委托给对应的策略对象
  return this.levelObj.calculate(this.salary);
};
var bouns = new Bouns();
bouns.setSalary(10000);
bouns.setlevelObj(new performanceA()); // 设置策略对象
console.log(bouns.getBouns()); // 40000

bouns.setlevelObj(new performanceB()); // 设置策略对象
console.log(bouns.getBouns()); // 30000

如上代码使用策略模式重构代码,可以看到代码职责更新分明,代码变得更加清晰。

4. Javascript版本的策略模式

//代码如下:
var obj = {
    "A": function(salary) {
      return salary * 4;
    },
    "B" : function(salary) {
      return salary * 3;
    },
    "C" : function(salary) {
      return salary * 2;
    }
};
var calculateBouns =function(level,salary) {
  return obj[level](salary);
};
console.log(calculateBouns('A',10000)); // 40000

可以看到代码更加简单明了;

策略模式指的是定义一系列的算法,并且把它们封装起来,但是策略模式不仅仅只封装算法,我们还可以对用来封装一系列的业务规则,只要这些业务规则目标一致,我们就可以使用策略模式来封装它们;

表单效验

比如我们经常来进行表单验证,比如注册登录对话框,我们登录之前要进行验证操作:比如有以下几条逻辑:

用户名不能为空

密码长度不能小于6位。

手机号码必须符合格式。

比如HTML代码如下:

<form action = "http://www.baidu.com" id="registerForm" method = "post">
    <p>
      <label>请输入用户名:</label>
      <input type="text" name="userName"/>
    </p>
    <p>
      <label>请输入密码:</label>
      <input type="text" name="password"/>
    </p>
    <p>
      <label>请输入手机号码:</label>
      <input type="text" name="phoneNumber"/>
    </p>
</form>

我们正常的编写表单验证代码如下:

var registerForm = document.getElementById("registerForm");
registerForm.onsubmit = function(){
  if(registerForm.userName.value === '') {
    alert('用户名不能为空');
    return;
  }
  if(registerForm.password.value.length < 6) {
    alert("密码的长度不能小于6位");
    return;
  }
  if(!/(^1[3|5|8][0-9]{9}$)/.test(registerForm.phoneNumber.value)) {
    alert("手机号码格式不正确");
    return;
  }
}

但是这样编写代码有如下缺点:

1.registerForm.onsubmit 函数比较大,代码中包含了很多if语句;

2.registerForm.onsubmit 函数缺乏弹性,如果增加了一种新的效验规则,或者想把密码的长度效验从6改成8,我们必须改registerForm.onsubmit 函数内部的代码。违反了开放-封闭原则。

3. 算法的复用性差,如果在程序中增加了另外一个表单,这个表单也需要进行一些类似的效验,那么我们可能又需要复制代码了;

下面我们可以使用策略模式来重构表单效验;

第一步我们先来封装策略对象;如下代码:

var strategy = {
  isNotEmpty: function(value,errorMsg) {
    if(value === '') {
      return errorMsg;
    }
  },
  // 限制最小长度
  minLength: function(value,length,errorMsg) {
    if(value.length < length) {
      return errorMsg;
    }
  },
  // 手机号码格式
  mobileFormat: function(value,errorMsg) {
    if(!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
      return errorMsg;
    }
  }
};

接下来我们准备实现Validator类,Validator类在这里作为Context,负责接收用户的请求并委托给strategy 对象,如下代码:

var Validator = function(){
  this.cache = []; // 保存效验规则
};
Validator.prototype.add = function(dom,rule,errorMsg) {
  var str = rule.split(":");
  this.cache.push(function(){
    // str 返回的是 minLength:6
    var strategy = str.shift();
    str.unshift(dom.value); // 把input的value添加进参数列表
    str.push(errorMsg); // 把errorMsg添加进参数列表
    return strategys[strategy].apply(dom,str);
  });
};
Validator.prototype.start = function(){
  for(var i = 0, validatorFunc; validatorFunc = this.cache[i++]; ) {
    var msg = validatorFunc(); // 开始效验 并取得效验后的返回信息
    if(msg) {
      return msg;
    }
  }
};

Validator类在这里作为Context,负责接收用户的请求并委托给strategys对象。上面的代码中,我们先创建一个Validator对象,然后通过validator.add方法往validator对象中添加一些效验规则,validator.add方法接收3个参数,如下代码:

validator.add(registerForm.password,'minLength:6','密码长度不能小于6位');

registerForm.password 为效验的input输入框dom节点;

minLength:6: 是以一个冒号隔开的字符串,冒号前面的minLength代表客户挑选的strategys对象,冒号后面的数字6表示在效验过程中所必须验证的参数,minLength:6的意思是效验 registerForm.password 这个文本输入框的value最小长度为6位;如果字符串中不包含冒号,说明效验过程中不需要额外的效验信息;

第三个参数是当效验未通过时返回的错误信息;

当我们往validator对象里添加完一系列的效验规则之后,会调用validator.start()方法来启动效验。如果validator.start()返回了一个errorMsg字符串作为返回值,说明该次效验没有通过,此时需要registerForm.onsubmit方法返回false来阻止表单提交。下面我们来看看初始化代码如下:

var validateFunc = function(){
  var validator = new Validator(); // 创建一个Validator对象
  /* 添加一些效验规则 */
  validator.add(registerForm.userName,'isNotEmpty','用户名不能为空');
  validator.add(registerForm.password,'minLength:6','密码长度不能小于6位');
  validator.add(registerForm.userName,'mobileFormat','手机号码格式不正确');

  var errorMsg = validator.start(); // 获得效验结果
  return errorMsg; // 返回效验结果
};
var registerForm = document.getElementById("registerForm");
registerForm.onsubmit = function(){
  var errorMsg = validateFunc();
  if(errorMsg){
    alert(errorMsg);
    return false;
  }
}

下面是所有的代码如下:

var strategys = {
  isNotEmpty: function(value,errorMsg) {
    if(value === '') {
      return errorMsg;
    }
  },
  // 限制最小长度
  minLength: function(value,length,errorMsg) {
    if(value.length < length) {
      return errorMsg;
    }
  },
  // 手机号码格式
  mobileFormat: function(value,errorMsg) {
    if(!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
      return errorMsg;
    }
  }
};
var Validator = function(){
  this.cache = []; // 保存效验规则
};
Validator.prototype.add = function(dom,rule,errorMsg) {
  var str = rule.split(":");
  this.cache.push(function(){
    // str 返回的是 minLength:6
    var strategy = str.shift();
    str.unshift(dom.value); // 把input的value添加进参数列表
    str.push(errorMsg); // 把errorMsg添加进参数列表
    return strategys[strategy].apply(dom,str);
  });
};
Validator.prototype.start = function(){
  for(var i = 0, validatorFunc; validatorFunc = this.cache[i++]; ) {
    var msg = validatorFunc(); // 开始效验 并取得效验后的返回信息
    if(msg) {
      return msg;
    }
  }
};

var validateFunc = function(){
  var validator = new Validator(); // 创建一个Validator对象
  /* 添加一些效验规则 */
  validator.add(registerForm.userName,'isNotEmpty','用户名不能为空');
  validator.add(registerForm.password,'minLength:6','密码长度不能小于6位');
  validator.add(registerForm.userName,'mobileFormat','手机号码格式不正确');

  var errorMsg = validator.start(); // 获得效验结果
  return errorMsg; // 返回效验结果
};
var registerForm = document.getElementById("registerForm");
registerForm.onsubmit = function(){
  var errorMsg = validateFunc();
  if(errorMsg){
    alert(errorMsg);
    return false;
  }
};

如上使用策略模式来编写表单验证代码可以看到好处了,我们通过add配置的方式就完成了一个表单的效验;这样的话,那么代码可以当做一个组件来使用,并且可以随时调用,在修改表单验证规则的时候,也非常方便,通过传递参数即可调用;

给某个文本输入框添加多种效验规则,上面的代码我们可以看到,我们只是给输入框只能对应一种效验规则,比如上面的我们只能效验输入框是否为空,validator.add(registerForm.userName,'isNotEmpty','用户名不能为空');但是如果我们既要效验输入框是否为空,还要效验输入框的长度不要小于10位的话,那么我们期望需要像如下传递参数:

validator.add(registerForm.userName,[{strategy:'isNotEmpty',errorMsg:'用户名不能为空'},{strategy: 'minLength:6',errorMsg:'用户名长度不能小于6位'}])

我们可以编写代码如下:

// 策略对象
var strategys = {
  isNotEmpty: function(value,errorMsg) {
    if(value === '') {
      return errorMsg;
    }
  },
  // 限制最小长度
  minLength: function(value,length,errorMsg) {
    if(value.length < length) {
      return errorMsg;
    }
  },
  // 手机号码格式
  mobileFormat: function(value,errorMsg) {
    if(!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
      return errorMsg;
    }
  }
};
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 strategys[strategy].apply(dom,strategyAry);
      });
    })(rule);
  }
};
Validator.prototype.start = function(){
  for(var i = 0, validatorFunc; validatorFunc = this.cache[i++]; ) {
  var msg = validatorFunc(); // 开始效验 并取得效验后的返回信息
  if(msg) {
    return msg;
  }
  }
};
// 代码调用
var registerForm = document.getElementById("registerForm");
var validateFunc = function(){
  var validator = new Validator(); // 创建一个Validator对象
  /* 添加一些效验规则 */
  validator.add(registerForm.userName,[
    {strategy: 'isNotEmpty',errorMsg:'用户名不能为空'},
    {strategy: 'minLength:6',errorMsg:'用户名长度不能小于6位'}
  ]);
  validator.add(registerForm.password,[
    {strategy: 'minLength:6',errorMsg:'密码长度不能小于6位'},
  ]);
  validator.add(registerForm.phoneNumber,[
    {strategy: 'mobileFormat',errorMsg:'手机号格式不正确'},
  ]);
  var errorMsg = validator.start(); // 获得效验结果
  return errorMsg; // 返回效验结果
};
// 点击确定提交
registerForm.onsubmit = function(){
  var errorMsg = validateFunc();
  if(errorMsg){
    alert(errorMsg);
    return false;
  }
}

注意:如上代码都是按照书上来做的,都是看到书的代码,最主要我们理解策略模式实现,比如上面的表单验证功能是这样封装的代码,我们平时使用jquery插件表单验证代码原来是这样封装的,为此我们以后也可以使用这种方式来封装表单等学习;

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • JavaScript设计模式之策略模式详解

    在程序设计中,我们也常常遇到这种情况,要实现某一个功能我们有很多种算法可以实现. 这些算法灵活多样,而且可以随意互相替换.这种解决方案就是所谓的策略模式. 策略模式的定义是:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换. /* * pre:策略模式 * 示例:公司计算奖金,分A.B.C 三种绩效,计算方式如下 * 绩效为A,奖金乘以系数5 * 绩效为B,奖金乘以系数4 * 绩效为C,奖金乘以系数3 */ //-------- 示例1 ---------- var calcula

  • 轻松掌握JavaScript策略模式

    定义:定义一系列的算法,把它们一个个封装成函数,也可把它们作为属性统一封装进一个对象,然后再定义一个方法,该方法可根据参数自动选择执行对应的算法. 一般用于在实现一个功能时,有很多个方案可选择的情况. 例子1:根据员工薪水salary.绩效等级S.A.B.C,来计算年终奖 //封装了所有算法的策略对象 var strategies = { 'S': function(salary){ return salary*4; }, 'A': function(salary){ return salary

  • 学习JavaScript设计模式(策略模式)

    何为策略?比如我们要去某个地方旅游,可以根据具体的实际情况来选择出行的线路. 1.策略模式的定义 如果没有时间但是不在乎钱,可以选择坐飞机. 如果没有钱,可以选择坐大巴或者火车. 如果再穷一点,可以选择骑自行车. 在程序设计中,我们也常常遇到类似的情况,要实现某一个功能有多种方案可以选择.比如一个压缩文件的程序,既可以选择zip算法,也可以选择gzip算法. 定义:策略模式定义一系列的算法,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算饭的客户. 策略模式有着广泛的应用.本

  • 详解JavaScript的策略模式编程

    我喜欢策略设计模式.我尽可能多的试着去使用它.究其本质,策略模式使用委托去解耦使用它们的算法类. 这样做有几个好处.他可以防止使用大条件语句来决定哪些算法用于特定类型的对象.将关注点分离开来,因此降低了客户端的复杂度,同时还可以促进子类化的组成.它提高了模块化和可测性.每一个算法都可以单独测试.每一个客户端都可以模拟算法.任意的客户端都能使用任何算法.他们可以互调.就像乐高积木一样. 为了实现策略模式,通常有两个参与者: 该策略的对象,封装了算法. 客户端(上下文)对象,以即插即用的方式能使用任

  • JavaScript设计模式之策略模式实例

    策略模式的意义是定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换. 一个小例子就能让我们一目了然. 回忆下jquery里的animate方法. 复制代码 代码如下: $( div ).animate( {"left: 200px"}, 1000, 'linear' );  //匀速运动 $( div ).animate( {"left: 200px"}, 1000, 'cubic' );  //三次方的缓动 这2句代码都是让div在1000ms内往右移动

  • 深入理解JavaScript系列(33):设计模式之策略模式详解

    介绍 策略模式定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化不会影响到使用算法的客户. 正文 在理解策略模式之前,我们先来一个例子,一般情况下,如果我们要做数据合法性验证,很多时候都是按照swith语句来判断,但是这就带来几个问题,首先如果增加需求的话,我们还要再次修改这段代码以增加逻辑,而且在进行单元测试的时候也会越来越复杂,代码如下: 复制代码 代码如下: validator = {             validate: function (value, ty

  • 利用策略模式与装饰模式扩展JavaScript表单验证功能

    简单的表单验证 html结构 <!-- validata.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Validata</title> </head> <body> <form id="form"> <labe

  • 学习JavaScript设计模式之策略模式

    把不变的部分和变化的部分隔开是每个设计模式的主题. 条条大路通罗马.我们经常会遇到解决一件事情有多种方案,比如压缩文件,我们可以使用zip算法.也可以使用gzip算法.其灵活多样,我们可以采用策略模式解决. 一.定义 定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换. 基于策略类模式的程序至少由两部分组成.第一个部分是一组策略类,策略类封装了具体的算法,并负责具体的计算过程.第二个部分是环境类Context,Context接收客户的请求,随后把请求委托给某一个策略类. 二.示例 计

  • javascript设计模式--策略模式之输入验证

    策略模式定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算饭的客户. 先定义一个简单的输入表单: <!DOCTYPE html> <html> <head> <meta charset="utf-"> <style> .form{ width: px; height: px; #margin: px auto; } .form-item-label{ width:px; text-align:

  • javascript设计模式之策略模式学习笔记

    1. 理解javascript中的策略模式 策略模式的定义是:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换. 使用策略模式的优点如下: 优点: 1. 策略模式利用组合,委托等技术和思想,有效的避免很多if条件语句. 2. 策略模式提供了开放-封闭原则,使代码更容易理解和扩展. 3. 策略模式中的代码可以复用. 一:使用策略模式计算奖金: 下面的demo是我在书上看到的,但是没有关系,我们只是来理解下策略模式的使用而已,我们可以使用策略模式来计算奖金问题: 比如公司的年终奖是根据

  • javascript设计模式之模块模式学习笔记

    我们通过单体模式理解了是以对象字面量的方式来创建单体模式的:比如如下的对象字面量的方式代码如下: var singleMode = { name: value, method: function(){ } }; 模块模式的思路是为单体模式添加私有变量和私有方法能够减少全局变量的使用:如下就是一个模块模式的代码结构: var singleMode = (function(){ // 创建私有变量 var privateNum = 112; // 创建私有函数 function privateFun

  • javascript设计模式之单体模式学习笔记

    单体模式提供了一种将代码组织为一个逻辑单元的手段,这个逻辑单元中的代码可以通过单一变量进行访问. 单体模式的优点是: 可以用来划分命名空间,减少全局变量的数量. 使用单体模式可以使代码组织的更为一致,使代码容易阅读和维护. 可以被实例化,且实例化一次. 什么是单体模式?单体模式是一个用来划分命名空间并将一批属性和方法组织在一起的对象,如果它可以被实例化,那么它只能被实例化一次. 但是并非所有的对象字面量都是单体,比如说模拟数组或容纳数据的话,那么它就不是单体,但是如果是组织一批相关的属性和方法在

  • JavaScript设计模式之策略模式实现原理详解

    俗话说,条条大路通罗马.在现实生活中,我们可以采用很多方法实现同一个目标.比如我们先定个小目标,先挣它一个亿.我们可以根据具体的实际情况来完成这个目标. 策略模式的定义 定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换. 举个例子:表单校验 在一个Web项目中,注册.登录等功能的实现都离不开表单提交.表单校验也是前端常常需要做的事.假设我们正在编写一个注册的页面,在点击提交按钮之前,有如下几条校验逻辑: 用户名不可为空,不允许以空白字符命名,用户名长度不能小于2位. 密码长度不能小

  • javascript设计模式之策略模式

    目录 一. 认识策略模式 二. 具体实现和思想 三. 策略模式的实际运用 四. 总结 一. 认识策略模式 策略模式的定义:定义一系列的算法,将他们一个个封装起来,使他们直接可以相互替换. 策略模式是开发中常用的第二种设计模式,它在开发中非常常见,由两部分组成.第一部分是策略类,封装了许多具体的,相似的算法.第二部分是环境类,接受客户请求,随后将请求委托给策略类.说的通俗一点就是将相同算法的函数存放在一个包装里边,每个函数用相同的方式拿出来,就叫做策略模式.下面我们来通过代码实现深入了解一下. 二

  • java设计模式之外观模式学习笔记

    外观模式: 又称门面模式: 外观Facade为子系统的一组接口提供一个一致界面,使得这组子系统易于使用(通过引入一个新的外观角色降低原系统复杂度,同时降低客户类与子系统的耦合度). 图片来源: 设计模式: 可复用面向对象软件的基础. 实现 案例需求: 租房 有过自己找房租房经历的同学能够体会得到找房是件很痛苦的事, 不光要挨个小区跑而且还要跟(二)房东讨价还价. 于是后来学聪明了, 不再自己挨门挨户的磨嘴皮子, 而是直接找像链家.我爱我家这样的房屋中介, 他们手上握有一定的房源, 我们只需付给他

  • 学习JavaScript设计模式之迭代器模式

    迭代器模式是指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示. JavaScript中的Array.prototype.forEach 一.jQuery中的迭代器 $.each([1, 2, 3], function(i, n) { console.log("当前下标为:"+ i + " 当前元素为:"+ n ); }); 二.实现自己的迭代器 var each = function(ary, callback) { for(var i

随机推荐