策略模式的概念
| 1、定义一系列的算法,把它们一个个 封装 起来,并且使它们可以互相替换 |
| |
| 2、策略模式的目的就是将算法的使用与算法的实现分离开,一个策略模式的程序至少由两部分组成。 |
| |
| 第一个部分是 策略类,策略类封装了具体的算法,并负责具体的计算过程。 |
| |
| 第二个部分是环境类 Context,Context 接受客户的请求,随后把请求委托给某一个策略类。 |
| |
| 3、要实现某一个功能,有多种方案可以选择。我们定义策略,把它们一个个封装起来,并且使它们可以相互转换。 |
策略模式的基础功能
| 当你负责的模块,基本满足以下情况时 可以使用策略模式 |
| |
| 1、各判断条件下的策略相互独立且可复用 |
| |
| 2、策略内部逻辑相对复杂 |
| |
| 3、策略需要灵活组合 |
| |
| 可参看下面的场景运用 |
策略模式的代码示例
| 1、人人喊打的 if-else 转为 策略模式 |
| |
| const activity = (type, price) => { |
| if (type === 'pre') { |
| return price * 0.95; |
| } else if (type === 'onSale') { |
| return price * 0.9; |
| } else if (type === 'back') { |
| return price * 0.85; |
| } else if (type === 'limit') { |
| return price * 0.8; |
| } |
| } |
| |
| 以上代码存在肉眼可见的问题:大量 if-else、可扩展性差、违背开放封闭原则等。 我们再使用策略模式优化 |
| |
| const activity = new Map([ |
| ['pre', (price) => price * 0.95], |
| ['onSale', (price) => price * 0.9], |
| ['back', (price) => price * 0.85], |
| ['limit', (price) => price * 0.8] |
| ]); |
| |
| const getActivityPrice = (type, price) => activity.get(type)(price); |
| |
| |
| activity.set('newcomer', (price) => price * 0.7); |
| 2、策略模式-校验表单 |
| |
| <html> |
| <head> |
| <title>策略模式-校验表单</title> |
| <meta content="text/html; charset=utf-8" http-equiv="Content-Type"> |
| </head> |
| <body> |
| <form id = "registerForm" method="post" action="http://xxxx.com/api/register"> |
| 用户名:<input type="text" name="userName"> |
| 密码:<input type="text" name="password"> |
| 手机号码:<input type="text" name="phoneNumber"> |
| <button type="submit">提交</button> |
| </form> |
| <script type="text/javascript"> |
| |
| const strategies = { |
| isNoEmpty: function (value, errorMsg) { |
| if (value === '') { |
| return errorMsg; |
| } |
| }, |
| isNoSpace: function (value, errorMsg) { |
| if (value.trim() === '') { |
| return errorMsg; |
| } |
| }, |
| minLength: function (value, length, errorMsg) { |
| if (value.trim().length < length) { |
| return errorMsg; |
| } |
| }, |
| maxLength: function (value, length, errorMsg) { |
| if (value.length > length) { |
| return errorMsg; |
| } |
| }, |
| isMobile: function (value, errorMsg) { |
| if (!/^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|17[7]|18[0|1|2|3|5|6|7|8|9])\d{8}$/.test(value)) { |
| return errorMsg; |
| } |
| } |
| } |
| |
| |
| class Validator { |
| constructor() { |
| this.cache = [] |
| } |
| add(dom, rules) { |
| for(let i = 0, rule; rule = rules[i++];) { |
| let strategyAry = rule.strategy.split(':') |
| let errorMsg = rule.errorMsg |
| this.cache.push(() => { |
| let strategy = strategyAry.shift() |
| strategyAry.unshift(dom.value) |
| strategyAry.push(errorMsg) |
| return strategies[strategy].apply(dom, strategyAry) |
| }) |
| } |
| } |
| start() { |
| for(let i = 0, validatorFunc; validatorFunc = this.cache[i++];) { |
| let errorMsg = validatorFunc() |
| if (errorMsg) { |
| return errorMsg |
| } |
| } |
| } |
| } |
| |
| |
| let registerForm = document.getElementById('registerForm') |
| |
| let validataFunc = function() { |
| let validator = new Validator() |
| validator.add(registerForm.userName, [{ |
| strategy: 'isNoEmpty', |
| errorMsg: '用户名不可为空' |
| }, { |
| strategy: 'isNoSpace', |
| errorMsg: '不允许以空白字符命名' |
| }, { |
| strategy: 'minLength:2', |
| errorMsg: '用户名长度不能小于2位' |
| }]) |
| validator.add(registerForm.password, [ { |
| strategy: 'minLength:6', |
| errorMsg: '密码长度不能小于6位' |
| }]) |
| validator.add(registerForm.phoneNumber, [{ |
| strategy: 'isMobile', |
| errorMsg: '请输入正确的手机号码格式' |
| }]) |
| return validator.start() |
| } |
| |
| registerForm.onsubmit = function() { |
| let errorMsg = validataFunc() |
| if (errorMsg) { |
| alert(errorMsg) |
| return false |
| } |
| } |
| </script> |
| </body> |
| </html> |
策略模式的总结
| 小结 |
| |
| 定义一系列算法,将其一一封装起来,并且使它们可相互替换。符合开放封闭原则。 |
| |
| 使用场景 |
| |
| 1、表单验证、存在大量 if-else 场景、各种重构 等。 |
| |
| 2、如果在一个系统里面有许多类,它们之间的区别仅在于它们的'行为',那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。 |
| |
| 3、一个系统需要动态地在几种算法中选择一种。 |
| |
| 4、表单验证 |
| |
| 优点 |
| |
| 1、利用组合、委托、多态等技术和思想,可以有效的避免多重条件选择语句 |
| |
| 2、提供了对开放-封闭原则的完美支持,将算法封装在独立的strategy中,使得它们易于切换,理解,易于扩展 |
| |
| 3、利用组合和委托来让 Context 拥有执行算法的能力,这也是继承的一种更轻便的代替方案 |
| |
| 缺点 |
| |
| 1、会在程序中增加许多策略类或者策略对象 |
| |
| 2、要使用策略模式,必须了解所有的strategy,必须了解各个strategy之间的不同点,这样才能选择一个合适的strategy |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构