『设计模式』—— 策略模式(在vue中应用)

1,659 阅读2分钟

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

核心:将算法的使用与算法的实现分离开来

一个策略模式的程序至少由两部分组成:
①策略类,策略类中封装了具体的算法,并负责具体的计算过程
②环境类,环境类接收外部的请求,随后把请求委托给某一个策略类

实现策略模式

很多公司的年终奖是根据员工的工资基数和年底绩效情况来发放的。例如,绩效为 S 的人年终奖有 4 倍工资,绩效为 A 的人年终奖有 3 倍工资,而绩效为 B 的人年终奖是 2 倍工资。假设财务部要求我们提供一段代码,来方便他们计算员工的年终奖
var S = function (salary) {
    return salary * 4;
};
var A = function (salary) {
    return salary * 3;
};
var B = function (salary) {
    return salary * 2;
};
var calculateBonus = function (func, salary) {
    return func(salary);
};
calculateBonus(S, 10000); // 输出:40000  

这里将策略直接封装成函数,将封装的函数当作参数传递进calculateBonus函数中,当我们对这些函数发出“调用”的消息时,不同的函数会返回不同的执行结果

用策略模式来进行表单校验

<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>
</body>
</html>
// 定义策略类
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;
        }
    }
};
// 定义环境类
var validataFunc = function () {
    var validator = new Validator(); // 创建一个 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; // 阻止表单提交
    }
};
// Validator 类
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;
        }
    }
};

Vue高级异步组件+策略模式

context.vue
<template>
  <div>
    <component :is="typeCom"/>
    <button @click="next">下一个</button>
  </div>
</template>
<script>
const TYPE = ["success", "warning", "info", "error"];
// 引入策略对象加载高级异步组件
import strategies from "@/strategies.js";
export default {
  data() {
    return {
      status: 0,
    };
  },
  computed: {
    // 计算属性充当环境类
    typeCom() {
      let index = TYPE[this.status];
      return strategies[index];
    },
  },
  methods: {
    next() {
      if (this.status === TYPE.length - 1) {
        this.status = 0;
      } else {
        this.status++;
      }
    },
  },
};
</script>

strategies.js

const loading = {
    template: '<div>加载中</div>'
}
const error = {
    template: '<div>载错误</div>'
}
export default {
    // 高级异步组件,参考-https://cn.vuejs.org/v2/guide/components-dynamic-async.html
    success: () => ({
        component: import('@/views/Success'),
        loading,
        error,
        delay: 200,
        timeout: 3000,
    }),
    warning: () => ({
        component: import("@/views/Warning"),
        loading,
        error,
        delay: 200,
        timeout: 3000,
    }),
    info: () => ({
        component: import("@/views/Info"),
        loading,
        error,
        delay: 200,
        timeout: 3000,
    }),
    error: () => ({
        component: import("@/views/Error"),
        loading,
        error,
        delay: 200,
        timeout: 3000,
    })
}

效果:

本来还想写个element表单验证的demo的,但是想了下,关于方法的策略模式在我们日常开发中的使用太多了,所以这里就不写了

参考

JavaScript设计模式与开发实践--曾探

设计模式在vue中的应用(三)

Vue动态异步组件实现思路

其他参考: vue 异步组件与vue-router 懒加载实践

若有错误或者对于vue对于策略模式更好的实现欢迎在评论区留言,最后感谢阅读!!!

设计模式是对语言不足的补充

—— Peter Norvig