设计模式-策略模式

策略模式

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

注释: “并且使它们可以相互替换”,这句话在很大程度上是相对于静态类型语言而言的。因为静态类型语言中有类型检查机制,所以各个策略类需要实现同样的接口。当它们的真正类型被隐藏在接口后面时,它们才能被相互替换。而在 JavaScript这种“类型模糊”的语言中没有这种困扰,任何对象都可以被替换使用。因此,JavaScript中的“可以相互替换使用”表现为它们具有相同的目标和意图。

一个基于策略模式的程序至少由两部分组成:第一个部分是一组策略类,策略类封装了具体
的算法,并负责具体的计算过程;第二个部分是环境类 Context,Context接受客户的请求,随后
把请求委托给某一个策略类

现在假设有一个计算年终奖的需求,绩效为 S的人年终奖有 4倍工资,绩效为 A的人年终奖有 3倍工资,而绩效为 B的人年终奖是 2倍工资,用策略模式实现这个需求

// 策略类
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

strategies策略类负责算法部分,它们都有相同的目标,就是返回绩效值。calculateBonus负责把客户端请求委托给策略类,用户在调用calculateBonus时只需要传入参数即可

策略类并不一定要是算法计算,只要有相同的目标都可以封装成策略类,比如前端在进行表单验证时,会判断用户的输入值是否满足要求,这个需求也非常适合用策略类实现

<form action="http:// xxx.com/register" id="registerForm" method="post">
  请输入用户名:<input type="text" name="userName" />
  请输入密码:<input type="text" name="password" />
  请输入手机号码:<input type="text" name="phoneNumber" />
  <button>提交</button>
</form>
// 策略对象
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); 
        // strategyAry => ['输入的值', 6, '用户名长度不能小于 6 位']
        // strategyAry => ['输入的值', '用户名不能为空']
        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: '用户名长度不能小于 6 位'
  }]);
  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;
  }
};
posted @ 2021-10-07 15:04  wmui  阅读(29)  评论(0编辑  收藏  举报