6

结构型:策略模式 - 风吹De麦浪

 1 year ago
source link: https://www.cnblogs.com/longbensong/p/17260684.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
  定义一系列的算法,将他们一个个封装起来,使他们直接可以相互替换。
  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,如写一个很简单的表单的校验

<!DOCTYPE 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判断来做相关的逻辑校验。

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

<!DOCTYPE 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. 策略模式有效避免多重条件选择语句,将算法封装在策略中 

如果您觉得阅读本文对您有帮助,请点一下推荐按钮,您的推荐将是我最大的写作动力,欢迎各位转载!

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK