前端也要學系列:設計模式之策略模式

来源:https://www.cnblogs.com/depsi/archive/2018/05/24/9080568.html
-Advertisement-
Play Games

做前端開發已經好幾年了,對設計模式一直沒有深入學習總結過。隨著架構相關的工作越來越多,越來越能感覺到設計模式成為了我前進道路上的一個阻礙。所以從今天開始深入學習和總結經典的設計模式以及面向對象的幾大原則。 今天第一天,首先來講策略模式。 什麼是策略模式? GoF四兄弟的經典《設計模式》中,對策略模式 ...


做前端開發已經好幾年了,對設計模式一直沒有深入學習總結過。隨著架構相關的工作越來越多,越來越能感覺到設計模式成為了我前進道路上的一個阻礙。所以從今天開始深入學習和總結經典的設計模式以及面向對象的幾大原則。

今天第一天,首先來講策略模式。

什麼是策略模式?

GoF四兄弟的經典《設計模式》中,對策略模式的定義如下:

定義一系列的演算法,把它們一個個封裝起來,並且使它們可互相替換。

上邊這句話,從字面來看很簡單。但是如何在開發過程中去應用,僅憑一個定義依然是一頭霧水。以筆者曾經做過的商戶進銷存系統為例:

某超市準備舉行促銷活動,市場人員經過調查分析制定了一些促銷策略:

  1. 購物滿100減10
  2. 購物滿200減30
  3. 購物滿300減50
  4. 。。。

收銀軟體的界面是這樣的(簡單示意):

我們應該如何計算實際消費金額?

最初的實現是這樣的:

//方便起見,我們把各個促銷策略定義為枚舉值:0,1,2...
var getActualTotal = function(onSaleType,originTotal){
    if(onSaleType===0){
        return originTotal-Math.floor(originTotal/100)*10
    }
    if(onSaleType===1){
        return originTotal-Math.floor(originTotal/200)*30
    }
    if(onSaleType===0){
        return originTotal-Math.floor(originTotal/300)*50
    }
}

getActualTotal(1,2680); //2208

上面這段代碼很簡單,而且缺點也很明顯。隨著我的滿減策略逐漸增多,getActualTotal函數會越變越大,而且充滿了if判斷,稍一疏忽就容易弄錯。

OK,有人說我很懶,雖然這樣不夠優雅但並不影響我的使用,畢竟滿減策略再多也多不到哪去。
我只能說,需求永遠不是程式員定的。。這時,市場人員說我們新版程式添加了會員功能,我們需要支持以下的促銷策略:

會員促銷策略:

  1. 會員充300返60,且首單打9折
  2. 會員充500返100,且首單打8折
  3. 會員充1000返300,且首單打7折
  4. ...

這個時候,如果你還在原先的getActualTotal函數中繼續添加if判斷,我想如果你的領導review你這段代碼,可能會懷疑自己當初怎麼把你招進來。。

OK,我們終於下定決心要重構促銷策略的代碼,我們可以這麼做:

var vipPolicy_0=function(originTotal){
    return originTotal-Math.floor(originTotal/100)*10
}
var vipPolicy_1=function(originTotal){
    return originTotal-Math.floor(originTotal/200)*30
}
...
//會員充1000返300
var vipPolicy_10=function(account,originTotal){
    if(account===0){
        account+=1300;
        return originTotal*0.9
    }else{
        account+=1300;
        return originTotal;
    }
    return originTotal-Math.floor(originTotal/200)*30
}
...
var vipPolicy_n=function(){
    ...
}

var getActualTotal=function(onSaleType,originTotal,account){
    switch(onSaleType){
        case 0:
            return vipPolicy_0(originTotal);
        case 1:
            return vipPolicy_0(originTotal);
        ...
        case n:
            return ...
        default:
            return originTotal;
    }
}

好了,現在我們每種策略都有自己獨立的空間了,看起來井井有條。但是還有兩個問題沒有解決:

  1. 隨著促銷策略的增加,getActualTotal的代碼量依然會越來越大
  2. 系統缺乏彈性,如果需要增加一種策略,那麼除了添加一個策略函數,還需要修改switch...case..語句

讓我們再來回顧一下策略模式的定義:

定義一系列的演算法,把它們一個個封裝起來,並且使它們可互相替換

在我們的例子中,每種促銷策略的實現方式是不一樣的,但我們最終的目的都是為了求得實際金額。策略模式可以把我們對促銷策略的演算法一個個封裝起來,並且使它們可互相替換而不影響我們對實際金額的求值,這正好是我們所需要的。

下麵我們用策略模式來重構上面的代碼:

var policies={
    "Type_0":function(originTotal){
        return originTotal-Math.floor(originTotal/100)*10 
    },
    "Type_1":function(originTotal){
        return originTotal-Math.floor(originTotal/200)*30 
    },
    ...
    "Type_n":function(originTotal){
        ... 
    }
}

var getActualTotal=function(onSaleType,originTotal,account){
    return policies["Type_"+onSaleType](originTotal,account)
}
//執行
getActualTotal(0,2680.00);//2208

分析上面的代碼我們發現,不管促銷策略如何增加,getActualTotal函數完全不需要再變化了。我們要做的,就是增加新策略的函數而已。

通過策略模式的代碼,我們消除了讓人反胃的大片條件分支語句,getActualTotal本身並沒有計算能力,而是將計算全權委托給了策略函數。

由此我們可以總結出策略模式實現的要點:

  1. 將變化的演算法封裝成獨立的策略函數,並負責具體的計算
  2. 委托函數,該函數接受客戶請求,並將請求委托給某一個具體的策略函數

用一張UML圖表示如下:

怎麼樣?現在看到上面這張圖是不是有了瞭然於胸的感覺?那就趕緊去試一試策略模式吧!


參考書籍:

  1. 《設計模式:可復用面向對象軟體的基礎》
  2. 《大話設計模式》
  3. 《Javascript設計模式與開發實踐》

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

-Advertisement-
Play Games
更多相關文章
  • ‘\r'是回車,前者使游標到行首,(carriage return)'\n'是換行,後者使游標下移一格,(line feed)\r 是回車,return\n 是換行,newline對於換行這個動作:unix下一般只有一個0x0A表示換行("\n"),windows下一般都是0x0D和0x0A兩個字元 ...
  • 筆者昨天下午臨走前安裝了vs 2017想要運行一下項目的NET後端來讓本機的前端直接對接後端,但是沒註意到運行vs後IIS直接占用了本機的80埠。第二天跑nodeJS的時候直接Error: listen EACCES 0.0.0.80報錯 筆者總結了一下遇到埠報錯的問題到解決問題的始末,遇到埠 ...
  • 我們在使用npm install 安裝模塊或插件的時候,有兩種命令把他們寫入到 package.json 文件裡面去,比如: --save-dev(-D) --save(-S) 在 package.json 文件裡面提現出來的區別就是,使用 --save-dev 安裝的 插件,被寫入到 devDep ...
  • 內容:NPM介紹,安裝web框架模塊,一些基本命令#####介紹NPM是隨同NodeJS一起安裝的包管理工具,能解決NodeJS代碼部署上的很多問題,常見的使用場景有以下幾種: • 允許用戶從NPM伺服器下載別人編寫的第三方包到本地使用。 • 允許用戶從NPM伺服器下載並安裝別人編寫的命令行程式到本 ...
  • 一:html文檔標簽結構 <html></html><!--文檔片頭信息,表示文檔內容是用什麼標簽寫的。--> <head></head><!--head是網頁定義網頁頭部信息,該信息不會顯示在網頁中,head標簽裡面可以嵌入其它標簽--> <title></title> <!--title標題標簽 ...
  • 1.效果圖如下: 2.代碼如下: ...
  • 其實是一種利用符號進行的類型轉換,轉換成數字類型~~true == 1~~false == 0~~"" == 0~~[] == 0~~undefined ==0~~!undefined == 1~~null == 0~~!null == 1 ...
  • 第 1 章 JavaScript簡介 使用 Node.js 搭建 Web 伺服器 JavaScript 的類型有數字、字元串、布爾值、函數和對象。還有 undefined 和 null ,以及數組、日期和正則表達式。 操作符 .cnblogs markdown table th:nth child( ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...