定义一系列算法,把它们一个个封装起来,并且使它们可以相互替换。一个基于策略模式的程序至少有两部分组成:
Context
假如你需要前往机场。 你可以选择乘坐公共汽车、 预约出租车或骑自行车。 这些就是你的出行策略。 你可以根据预算或时间等因素来选择其中一种策略。
这里我们以年终奖的计算为例进行实现:
假设绩效为 S
的人年终奖有 4 倍工资; A
的人年终奖有 3 倍工资; B
的人年终奖有 2 倍工资。
按照策略模式的基本组成我们进行分解:
// 第一步: 封装策略类 const strategies = { S: salary => { return salary * 4; }, A: salary => { return salary * 3; }, B: salary => { return salary * 2; } }; // 第二步:这里我们用calculateBonus函数充当Context来接收用户请求 const calculateBonus = (level, salary) => { return strategies[level](salary); }; console.log(calculateBonus("S", 20000)); // 输出:80000 console.log(calculateBonus("A", 10000)); // 输出:30000 复制代码
策略模式指的是定 义一系列算法 。但在实际的开发中,我们通常会把算法的含义扩散开来,使策略模式也可以用来封装一系列的 业务规则 。只要这些业务规则指向的目标一致,而且可以被替换使用。
在 web 应用中,涉及表单的部分几乎都离不开校验。
假设我们正在编写一个注册页面,在点击注册按钮前,需要校验以下几条逻辑:
// 第一步: 封装策略类 const strategies = { isNonEmpty: (value, errorMsg) => { if (value === "") { return errorMsg; } }, minLength: (value, errorMsg) => { if (value.length < 6) { return errorMsg; } }, isMobile: (value, errorMsg) => { if (!/(^1[3|5|8][0-9]{9}$)/.test(value)) { return errorMsg; } } }; // 第二步:封装Validator类充当Context来接收用户请求并委托给strategy对象 class Validator { constructor() { this.cache = []; } add(dom, strategy, errorMsg) { this.cache.push(() => { const args = [dom.value, errorMsg]; return strategies[strategy].apply(dom, args); }); } start() { this.cache.forEach(fn => { const msg = fn(); if (msg) { return msg; } }); } } // 使用 const registerForm = document.getElementById("registerForm"); function validataFunc() { const validator = new Validator(); validator.add(registerForm.userName, "isNonEmpty", "用户名不能为空"); validator.add(registerForm.password, "minLength", "密码长度不能少于6位"); validator.add(registerForm.phone, "isMobile", "手机号码格式不正确"); const error = validator.start(); return error; } registerForm.onsubmit = () => { const errorMsg = validataFunc(); if (errorMsg) { alert(errorMsg); return false; } }; 复制代码
可以看到,我们仅仅通过 配置 的方式就完成了一个表单的校验。
在JavaScript语言的策略模式中,策略类往往被函数所代替,这时策略模式就成了一种“隐形”的模式。尽管如此,从根本上理解策略模式的理念不仅让我们对模式有这更透彻的了解,也可以让我们明白使用函数的好处。
我来评几句
登录后评论已发表评论数()