很快,迎来了 JavaScript 设计模式系列的第二篇 —— 策略模式 …
什么是策略模式
策略模式定义:
定义一系列算法,把它们一个个封装起来,并且使它们可以相互替换
策略模式一般由两部分组成:
-
封装不同策略的策略组(使得代码复用性、可扩展、可维护性提高,避免大量 CV 代码的情况)
-
Context(委托算法,执行策略)
什么时候使用策略模式 ?
策略模式广泛应用于程序研发中,当出现需要根据不同的前置条件执行不同的算法得到结果时,使用策略模式可以让你的代码更加优雅
怎么?不信? 那就上点代码让你感受一下 💩 山的力量!
假设一个函数负责 Consul (服务发现)和 LB (负载均衡)
这里 Consul 函数就是 Context,各种 LB 算法就是策略组
传入服务唯一标识和负载均衡算法
返回服务器实例 IP 地址
如想了解 LB 相关知识,可以看看这篇文章 [深入浅出 LB]手把手带你实现一个负载均衡器
function consul(serviceId, algorithm) { if (algorithm === 'random') { return 'xxx.xxx.xxx.xxx'; } else if (algorithm === 'weightedRoundRobin') { return 'xxx.xxx.xxx.xxx'; } else if (algorithm === 'ipHash') { return 'xxx.xxx.xxx.xxx'; } else if (algorithm === 'urlHash') { return 'xxx.xxx.xxx.xxx'; } else if (algorithm === 'leastConnection') { return 'xxx.xxx.xxx.xxx'; } else if (algorithm === 'fair') { return 'xxx.xxx.xxx.xxx'; } }
|
可以发现,这段代码存在着一些明显的问题
-
Consul 函数过于庞大,不符合单一职责原则,难维护
-
堆砌了过多的 IF-ELSE 语句,代码看起来比较冗余
-
负载均衡算法复用性较差,如果其他地方需要用到…(你不会直接 CV 吧 🤓
针对以上问题,我们可以利用策略模式加以优化
-
首先将各 LB 算法封装成独立的函数,提高复用性
-
建立算法名称 -> 算法执行函数映射,干掉冗余的 IF-ELSE(简直就是 IF-ELSE 的救世主
-
简化 Consul 函数,具体算法对于 Consul 来说是“隐形”的,单一职责
-
可拓展性提高,如需拓展更多算法,仅需引入算法和添加 Map 中的配置
function random(serviceId) { return 'xxx.xxx.xxx.xxx'; } function weightedRoundRobin(serviceId) { return 'xxx.xxx.xxx.xxx'; } function ipHash(serviceId) { return 'xxx.xxx.xxx.xxx'; } function urlHash(serviceId) { return 'xxx.xxx.xxx.xxx'; } function leastConnection(serviceId) { return 'xxx.xxx.xxx.xxx'; } function fair(serviceId) { return 'xxx.xxx.xxx.xxx'; }
const ALGORITHM = { RANDOM: 'random', WEIGHTED_ROUND_ROBIN: 'weightedRoundRobin', IP_HASH: 'ipHash', URL_HASH: 'urlHash', LEAST_CONNECTION: 'leastConnection', FAIR: 'fair', };
const ALGORITHM_MAP = { [ALGORITHM.RANDOM]: random, [ALGORITHM.WEIGHTED_ROUND_ROBIN]: weightedRoundRobin, [ALGORITHM.IP_HASH]: ipHash, [ALGORITHM.URL_HASH]: urlHash, [ALGORITHM.LEAST_CONNECTION]: leastConnection, [ALGORITHM.FAIR]: fair, };
function consul(serviceId, algorithm) { return ALGORITHM_MAP[algorithm](serviceId); }
|
策略模式应用场景之表单校验
粗糙的表单校验
let loginFrom = document.getElementId('loginFrom'); loginFrom.onsubmit = function (e) { const username = document.getElementId('username'); const pwd = document.getElementId('pwd'); const mobile = document.getElementId('mobile'); if (username === '') { alert('用户名不可为空'); return false; } if (pwd.length < 6) { alert('密码长度不能少于6位'); return false; } if ( !/^1(3\d|4[5-9]|5[0-35-9]|6[2567]|7[0-8]|8\d|9[0-35-9])\d{8}$/.test(mobile) ) { alert('手机号码格式不正确'); return false; } };
|
使用策略模式优化后的表单校验
Context:validate
策略组:strategy
const strategy = { isEmpty: function ({ value }, errMsg) { if (value === '') { return errMsg; } }, isMobile: function ({ value }, errMsg) { if ( !/^1(3\d|4[5-9]|5[0-35-9]|6[2567]|7[0-8]|8\d|9[0-35-9])\d{8}$/.test(value) ) { return errMsg; } }, isLongerThanMinLength: function ({ value, len }, errMsg) { if (value.length < len) { return errMsg; } }, };
class Validator { constructor() { this.validateItems = []; }
add(params, strategyKey, errMsg) { this.validateItems.push({ params, strategyKey, errMsg }); }
start() { for (const item of this.validateItems) { const { params, strategyKey, errMsg } = item; const msg = strategy[strategyKey](params, errMsg); if (msg) { return msg; } } } }
function validate() { const username = document.getElementId('username'); const pwd = document.getElementId('pwd'); const mobile = document.getElementId('mobile');
const validator = new Validator(); validator.add({ value: username }, 'isEmpty', '用户名不可为空'); validator.add( { value: pwd, len: 6 }, 'isLongerThanMinLength', '密码长度不能少于6位' ); validator.add({ value: mobile }, 'isMobile', '手机号码格式不正确');
const errMsg = validator.start(); if (errMsg) { alert(errMsg); } else { alert('校验通过'); } }
validate();
|
策略模式和多态的区别
策略模式强调的是做同一件事的不同且不重复的方法
多态是一种语言机制,有的不支持多态的语言也一样要实现策略模式
策略处于程序设计层次,多态处于语言语法层次
总结
策略模式的优点
-
策略模式利用组合、委托和多态等技术和思想,可以有效避免多重且冗余的 IF-ELSE
-
策略模式提供了对开放——封闭原则的完美支持,将算法封装在独立的策略中。可以在不修改原代码的情况下,灵活增加新算法。提高了它们的复用性、和可拓展性,也更容易切换和理解。
-
策略模式中的算法也可以复用在工程的其他地方,避免大量重复的 CV 工作
-
在策略模式中利用组合和委托来让 Context 拥有执行算法的能力,这也是继承的一种更轻便的替代方案
策略模式的缺点
设计模式系列文章推荐
掘金:前端 LeBron
知乎:前端 LeBron
持续分享技术博文,关注微信公众号 👇🏻