• 行为型:策略模式


    定义

      定义一系列的算法,将他们一个个封装起来,使他们直接可以相互替换。
     
    1. 算法:就是写的逻辑可以是你任何一个功能函数的逻辑
    2. 封装:就是把某一功能点对应的逻辑给抽出来
    3. 可替换:建立在封装的基础上,这些独立的算法可以很方便的替换
    通俗的理解就是,把你的算法(逻辑)封装到不同的策略中,在不同的策略中是互相独立的,这样我们封装的每一个算法是可以很方便的复用。

    策略模式主要解决在有多种情况下,使用 if...else 所带来的复杂和难以维护

    它的优点是算法可以自由切换,同时可以避免多重if...else判断,且具有良好的扩展性。

    看一个真实场景--最简单的策略模式

    我们有一个根据不同的类型返回不同价格的一个方法

    function getPrice (type) {
    if (type === 1) {
    // code 或许每个分支要处理很多逻辑
    }
    if (type === 2) {
    // code
    }
    if (type === 3) {
    // code
    }
    if (type === 4) {
    // code
    }
    if (type === 5) {
    // code
    }
    if (type === 6) {
    // code
    }
    if (type === 7) {
    // code
    }
    }

    代码上看确实没有什么问题,但是如果需要增加一种新的类型,我们就会一个if判断,导致这个方法可能会过于庞大,后期不太好维护。

    其次代码每次都是从上往下走,可能存在前面某个判断出现问题(如某个 && 的判断变量null 之类),导致后面的代码没有走,所以这种影响还是挺大的。

    用了这么多 if-else,我们的目的是什么?是不是就是为了把传进来的参数的值-对应的处理函数,这个映射关系给明确下来?在 JS 中我们可以通过对象映射的形式来做,如下代码

    /*
    1、把 if else 的代码快优化为一个一个的映射
    2、把if else 里面的逻辑抽离成一个独立的函数,这样方便其他模块或者分支使用
    */
    function getPrice (type) {
    const actionMap = {
    '1': action1,
    '2': action2,
    '3': action3,
    '4': action4,
    '5': action5,
    '6': action6,
    '7': action7,
    }
    const params = {}
    return actionMap[type](params)
    }

    这种代码结构变得易读、易维护。

    这就是最简单的策略模式

    模拟表单校验逻辑

    如果不把逻辑封装起来,那么我们在判断的时候会写很多的if else,如写一个很简单的表单的校验

    html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
    <meta content="yes" name="apple-mobile-web-app-capable">
    <meta content="black" name="apple-mobile-web-app-status-bar-style">
    <meta content="telephone=no,email=no" name="format-detection">
    <meta name="App-Config" content="fullscreen=yes,useHistoryState=yes,transition=yes">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>title>
    <link href="css/style.css" rel="stylesheet">
    head>
    <body>
    <div>
    <form action="" id="form">
    姓名:<input type="text" id="username"><br>
    密码:<input type="password" id="password1"><br>
    确认密码:<input type="password" id="password2"><br>
    手机号:<input type="text" id="phone"><br>
    <input type="submit" value="提交">
    form>
    div>
    <script>
    function getValue (id) {
    return document.getElementById(id).value;
    }
    var formData = document.getElementById('form')
    formData.onsubmit = function () {
    var name = getValue('username');
    var pwd1 = getValue('password1');
    var pwd2 = getValue('password2');
    var tel = getValue('phone');
    if (name.replace(/(^\s*)|(\s*$)/g, "") === "") {
    alert('用户名不能为空')
    return false
    }
    if (pwd1.replace(/(^\s*)|(\s*$)/g, "") === "") {
    alert('密码不能为空')
    return false
    }
    if (pwd2.replace(/(^\s*)|(\s*$)/g, "") === "") {
    alert('确认密码不能为空')
    return false
    }
    if (pwd2 !== pwd1) {
    alert('确认密码与原密码不相同!')
    return false
    }
    if (tel.replace(/(^\s*)|(\s*$)/g, "") === "") {
    alert('手机号码不能为空')
    return false
    }
    if (!/^1[3,4,5,7,8,9][0-9]\d{8}$/.test(tel)) {
    alert('手机号码格式不正确')
    return false
    }
    alert('注册成功')
    }
    script>
    body>
    html>

    只是4个字段,我们用了 6个if判断来做相关的逻辑校验。

    仔细观察发现很多校验的逻辑是一致的,所以我们可以把他封装起来,用策略模式修改成如下

    html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
    <meta content="yes" name="apple-mobile-web-app-capable">
    <meta content="black" name="apple-mobile-web-app-status-bar-style">
    <meta content="telephone=no,email=no" name="format-detection">
    <meta name="App-Config" content="fullscreen=yes,useHistoryState=yes,transition=yes">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>title>
    <link href="css/style.css" rel="stylesheet">
    head>
    <body>
    <div>
    <form action="" id="form">
    姓名:<input type="text" id="username"><br>
    密码:<input type="password" id="password1"><br>
    确认密码:<input type="password" id="password2"><br>
    手机号:<input type="text" id="phone"><br>
    <input type="submit" value="提交">
    form>
    div>
    <script>
    let formData = document.getElementById('form')
    function getValue(id) {
    return document.getElementById(id).value;
    }
    function Validate() { }
    Validate.prototype.rules = {
    // 是否手机号
    isMobile: function (str) {
    let rule = /^1[3,4,5,7,8,9][0-9]\d{8}$/;
    return rule.test(str);
    },
    // 是否必填
    isRequired: function (str) {
    // 除去首尾空格
    let value = str.replace(/(^\s*)|(\s*$)/g, "");
    return value !== "";
    },
    // 最小长度
    minLength: function (str, length) {
    let strLength = str.length;
    return strLength >= length;
    },
    // 是否相等
    isEqual: function (...args) {
    let equal = args.every(function (value) {
    return value === args[0];
    })
    return equal;
    }
    }
    Validate.prototype.test = function (rules) {
    let _this = this;
    let valid; // 保存校验结果
    for (let key in rules) { // 遍历校验规则对象
    for (let i = 0; i < rules[key].length; i++) { // 遍历每一个字段的校验规则
    let ruleName = rules[key][i].rule; // 获取每一个校验规则的规则名
    let value = rules[key][i].value; // 获取每一个校验规则的校验值
    if (!Array.isArray(value)) { // 统一校验值为数组类型
    value = new Array(value)
    }
    let result = _this.rules[ruleName].apply(this, value); // 调用校验规则方法进行校验
    if (!result) { // 如果校验不通过,就获取校验结果信息,并立即跳出循环不再执行,节约消耗
    valid = {
    errValue: key,
    errMsg: rules[key][i].message
    }
    break;
    }
    }
    if (valid) { // 如果有了校验结果,代表存在不通过的字段,则立即停止循环,节约消耗
    break;
    }
    }
    return valid; // 把校验结果反悔出去
    }
    formData.onsubmit = function () {
    event.preventDefault()
    let validator = new Validate();
    let result = validator.test({
    'username': [{ rule: 'isRequired', value: this.username.value, message: '用户名不能为空!' }],
    'password1': [
    { rule: 'isRequired', value: this.password1.value, message: '密码不能为空!' },
    { rule: 'minLength', value: [this.password1.value, 6], message: '密码长度不能小于6个字符!' }
    ],
    'password2': [
    { rule: 'isRequired', value: this.password2.value, message: '确认密码不能为空!' },
    { rule: 'minLength', value: [this.password2.value, 6], message: '确认密码长度不能小于6个字符!' },
    { rule: 'isEqual', value: [this.password2.value, this.password1.value], message: '确认密码与原密码不相同!' }
    ],
    'isMobile': [
    { rule: 'isRequired', value: this.phone.value, message: '手机号不能为空!' },
    { rule: 'isMobile', value: this.phone.value, message: '手机号格式不正确!' }
    ]
    })
    if (result) {
    console.log(result);
    } else {
    console.log('校验通过');
    }
    }
    script>
    body>
    html>

    下次我们增加其他的字段也只是增加规则而已,而不会去修改判断的业务逻辑。

    小结

    1. 将一个个算法封装起来,提高代码复用率,减少代码冗余
    2. 策略模式可看作为if/else判断的另一种表现形式,在达到相同目的的同时,极大的减少了代码量以及代码维护成本

    3. 策略模式有效避免多重条件选择语句,将算法封装在策略中 


    如果您觉得阅读本文对您有帮助,请点一下推荐按钮,您的推荐将是我最大的写作动力,欢迎各位转载!
  • 相关阅读:
    十四天学会C++之第三天(数组和字符串)
    GBase 8c 检查集群健康状态
    LrC 13 & ACR 16:点颜色
    【Linux】进程间通信 -- 管道
    python 如何解析含有重复key的json
    web网页设计期末课程大作业 HTML+CSS+JavaScript 美食餐饮文化主题网站设计 学生DW静态网页设计
    【计算机组成与设计】-第五章 memory hierarchy(二)
    算法作业1-2 字典序问题
    记一次线上故障--HashMap在多线程条件下运行造成CPU 100%
    scanf输入时的小细节
  • 原文地址:https://www.cnblogs.com/longbensong/p/17260684.html