行為型:策略模式

来源:https://www.cnblogs.com/longbensong/archive/2023/03/27/17260684.html
-Advertisement-
Play Games

定義 定義一系列的演算法,將他們一個個封裝起來,使他們直接可以相互替換。 演算法:就是寫的邏輯可以是你任何一個功能函數的邏輯 封裝:就是把某一功能點對應的邏輯給抽出來 可替換:建立在封裝的基礎上,這些獨立的演算法可以很方便的替換 通俗的理解就是,把你的演算法(邏輯)封裝到不同的策略中,在不同的策略中是互相獨 ...


定義

  定義一系列的演算法,將他們一個個封裝起來,使他們直接可以相互替換。  
  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. 策略模式有效避免多重條件選擇語句,將演算法封裝在策略中 

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 事先申明:所有android 類型的學習記錄全部基於《第一行代碼 Android》第三版,在此感謝郭霖老師的書籍幫助。 1.手動創建Activity 在Project類型目錄中尋找到 項目/app/src/main/java/com.example.activitytest 在 com.exampl ...
  • 【導讀】 日常運營過程中,消息推送是App觸達用戶的常用手段,無論是新功能的通知,還是活動的提醒,都可以通過推送告知用戶。千人千面的個性化推送,對於提升用戶粘性、用戶轉化等指標都具有明顯正向效果。越來越多的應用選擇華為Push用戶增長服務作為拉新、促活的首選渠道,他們選擇的理由是什麼?他們又是如何持 ...
  • 我為什麼推薦Nuxt3? 大家好,我今天想和你們分享一個非常棒的前端框架——Nuxt3。自從我接觸了Nuxt3,我發現它在前端開發領域具有很多優點。我想逐一向你們介紹Nuxt3的優勢,並向大家推薦一些學習資源。 Nuxt3的優點 1、基於Vue3: Nuxt3是基於Vue.js 3開發的,Vue.j ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 今天給大家帶來的是Vue 3 中的極致防抖/節流(含常見方式防抖/節流)這篇文章,文章中不僅會講述原來使用的防抖或節流方式,還會帶來新的一種封裝方式,使用起來更簡單、更清晰。 前言 在前端的開發過程中,在涉及到與用戶交互的過程中是基本上都 ...
  • JSS 是什麼 簡單來說,一句話概括CSS in JS (JSS),就是"行內樣式"(inline style)和"行內腳本"(inline script)。 因為,自從React出現以後,基於組件化的要求,強制把HTML、CSS、JavaScript捆綁在一起,在同一個文件裡面,封裝了結構、樣式、 ...
  • 現代瀏覽器用FileReader,老版本IE載入adodb.stream用ISO-8859-1~15字元集處理讀取,要註意與Unicode的編碼衝突。new bing會失憶會裝傻。 ...
  • el-radio el-radio中,已經選中的按鈕無法通過再次點擊去取消選擇,所以要滿足條件需要改變成對應的點擊事件。 <template> <div id="app"> <el-radio v-model="radio" :label="true" @click.native.prevent=" ...
  • 插槽基本介紹 在開發中,我們會經常封裝一個個可復用的組件: 前面我們會通過props傳遞給組件一些數據,讓組件來進行展示; 但是為了讓這個組件具備更強的通用性,我們不能將組件中的內容限製為固定的div、span等等這些元素; 比如某種情況下我們使用組件,希望組件顯示的是一個按鈕,某種情況下我們使用組 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...