欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2023-11-17 13:30:10
1. 理解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插件表单验证代码原来是这样封装的,为此我们以后也可以使用这种方式来封装表单等学习;

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