策略模式

字节踏月客
• 阅读 962
定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。

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

// S奖金
let performanceS = function () {};
performanceS.prototype.caculate = function (salary) {
    return salary * 4;
};
// B奖金
let performanceB = function () {};
performanceB.prototype.caculate = function (salary) {
    return salary * 2;
};

let Bonus = function () {
    this.salary = null; // 基础薪资
    this.strategy = null; // 对应的策略方法
};
Bonus.prototype.setSalary = function (salary) {
    this.salary = salary;
};

Bonus.prototype.setStrategy = function (strategy) {
    this.strategy = strategy;
};

Bonus.prototype.getBonus = function () {
    return this.strategy.caculate(this.salary);
};

let bonus = new Bonus();
bonus.setSalary(1000);
bonus.setStrategy(new performanceS());
console.log(bonus.getBonus());

bonus.setStrategy(new performanceB());
console.log(bonus.getBonus());

JavaScript 中使用策略

let strategies = {
    S: function (salary) {
        return salary * 4;
    },
    B: function (salray) {
        return salray * 2;
    },
};

var calculateBons = function (key, salary) {
    return strategies[key](salary);
};

console.log(calculateBons('S', 1000));
console.log(calculateBons('B', 1000));

策略模式对表单的校验

常使用的表单校验

<html>
    <body>
        <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>
        <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>

使用策略模式

  1. 重构表单的校验
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;
        }
    },
};
  1. 准备实现 Validator 类

    Validator 类在这里作为 Context,负责接收用户的请求并委托给 strategy 对象。在给出 Validator 类的代码之前,有必要提前了解用户是如何向 Validator类发送请求的,这有助于我们知道如何去编写 Validator 类的代码。
var validataFunc = function () {
    var validator = new Validator(); // 创建一个 validator 对象
    /***************添加一些校验规则****************/
    validator.add(registerForm.userName, 'isNonEmpty', '用户名不能为空');
    validator.add(registerForm.password, 'minLength:6', '密码长度不能少于 6 位');
    validator.add(registerForm.phoneNumber, 'isMobile', '手机号码格式不正确');
    var errorMsg = validator.start(); // 启动校验获得校验结果
    return errorMsg; // 返回校验结果
};
var registerForm = document.getElementById('registerForm');
registerForm.onsubmit = function () {
    var errorMsg = validataFunc(); // 如 果 errorMsg 有确切的返回值,说明未通过校验
    if (errorMsg) {
        alert(errorMsg);
        return false; // 阻止表单提交
    }
};

var Validator = function () {
    this.cache = []; // 保存校验规则
};
Validator.prototype.add = function (dom, rule, errorMsg) {
    var ary = rule.split(':'); // 把 strategy 和参数分开
    this.cache.push(function () {
        // 把校验的步骤用空函数包装起来,并且放入 cache
        var strategy = ary.shift(); // 用户挑选的 strategy
        ary.unshift(dom.value); // 把 input 的 value 添加进参数列表
        ary.push(errorMsg); // 把 errorMsg 添加进参数列表
        return strategies[strategy].apply(dom, ary); // 此处调用重构的校验
    });
};
Validator.prototype.start = function () {
    for (var i = 0, validatorFunc; (validatorFunc = this.cache[i++]); ) {
        var msg = validatorFunc(); // 开始校验,并取得校验后的返回信息
        if (msg) {
            // 如果有确切的返回值,说明校验没有通过
            return msg;
        }
    }
};
  1. 一个文本有多种校验规则

    // 需要校验
    validator.add(registerForm.userName, [
     {
         strategy: 'isNonEmpty',
         errorMsg: '用户名不能为空',
     },
     {
         strategy: 'minLength:6',
         errorMsg: '用户名长度不能小于 10 位',
     },
    ]);
    // 对add代码修改
    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;
     }
    };

总结

  • 策略模式利用组合、委托和多态等技术和思想,可以有效地避免多重条件选择语句。
  • 策略模式提供了对开放—封闭原则的完美支持,将算法封装在独立的 strategy 中,使得它
    们易于切换,易于理解,易于扩展。
  • 策略模式中的算法也可以复用在系统的其他地方,从而避免许多重复的复制粘贴工作。
  • 在策略模式中利用组合和委托来让 Context 拥有执行算法的能力,这也是继承的一种更轻
    便的替代方案。

参考:JavaScript设计模式与实践

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
6个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Easter79 Easter79
3年前
Vue 骚技巧,策略模式实现动态表单验证
!(https://oscimg.oschina.net/oscnet/5e0568a314054f2d995c1562bda18f70.png)策略模式(StrategyPattern)又称政策模式,其定义一系列的算法,把它们一个个封装起来,并且使它们可以互相替换。封装的策略算法一般是独立的,策略模式根据输入来调整采用哪个算法。
Wesley13 Wesley13
3年前
Java设计模式
一、策略模式(让算法与对象独立)    策略模式定义了算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。!(http://static.oschina.net/uploads/space/2016/1108/180244_oYm8_1789589.png)二、观察者模式(让你的对象知悉现状) 
Wesley13 Wesley13
3年前
00_设计模式之语言选择
设计模式之语言选择设计模式简介背景设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。设计模式(Designpattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的
Wesley13 Wesley13
3年前
Java 设计模式系列(十二)策略模式(Strategy)
Java设计模式系列(十二)策略模式(Strategy)策略模式属于对象的行为模式。其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。一、策略模式的结构策略模式是对算
京东云开发者 京东云开发者
1个月前
设计模式-策略模式
作者:京东工业孙磊一、概念策略模式(StrategyPattern)也称为(PolicyParttern)。它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变换,不会影响到使用算法的客户。策略模式属性行为模式。策略模式结构图\二、实际
京东云开发者 京东云开发者
6个月前
设计模式-策略模式
作者:京东工业孙磊一、概念策略模式(StrategyPattern)也称为(PolicyParttern)。它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变换,不会影响到使用算法的客户。策略模式属性行为模式。策略模式结构图\二、实际
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
京东云开发者 京东云开发者
2个月前
设计模式-策略模式
作者:京东工业孙磊一、概念策略模式(StrategyPattern)也称为(PolicyParttern)。它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变换,不会影响到使用算法的客户。策略模式属性行为模式。策略模式结构图\二、实际
字节踏月客
字节踏月客
Lv1
他心里若一直有个人我怎么闯都让他无动于衷
文章
4
粉丝
0
获赞
0