【狀態模式】拯救if-else堆出來的屎山代碼

前言

我想大家平時都在開發重都遇見過屎山代碼,這些屎山代碼一般都是由于復雜且龐大的if-else造成的,狀態模式,是一種很好的優化屎山代碼的設計模式,本文將采用兩個業務場景的示例來講解如何使用狀態模式拯救屎山代碼。

目錄

前言

1.網購業務場景

1.1.需求

1.2.if else的實現

1.3.狀態模式的實現

2.電梯業務場景

2.1.需求

2.2.if else的實現

2.3.狀態模式的實現


1.網購業務場景

1.1.需求

我們來假設一個網購的業務場景,需求如下:

  • 流程為付款、再發貨、在收貨,流程必須按照以上順序,也就是說發貨后不能支付、收貨后不能發貨和支付

  • 付款后不能重復付款、發貨后不能重復發貨、收貨后不能重復收貨

1.2.if else的實現

這里我們設計一個Order訂單類,用int型的state來表示狀態,當然也可以用一個枚舉類來表示狀態會更規范一點,這里為了方便而已。

public class Order {//1 未付款//2 已付款//3 未發貨//4 已發貨//5 未收貨//6 已收貨private int state;
?public int getState() {return state;}
?public void setState(int state) {this.state = state;}
}

以收貨方法為例,業務邏輯實現出來會是:

public void receive(Order order){if(order.getState()==2){if(order.getState()==4){if(order.getState()==5){System.out.println("收貨成功");}else{System.out.println("已收貨,無法重復收貨");}}else{System.out.println("未發貨");}}else{System.out.println("未付款");}
}

可以看到一座小屎山代碼已經初具規模,但凡狀態再多一點、業務邏輯再復雜一點,這座屎山將會基本不具備可讀性。

1.3.狀態模式的實現

其實仔細觀察可以發現,很多時候狀態往往是和實體的行為是相關的。之所以引入狀態,我們是希望實體在不同的狀態時呈現出不同的行為。

以上面的場景為例,在支付狀態下,我們希望實體能呈現出支付相關的能力;在發貨狀態下呈現出發貨相關的能力;在收貨狀態下呈現出收貨相關的能力......

所以完全可以把狀態和能力封裝在一起,從而省掉外界的if-else判斷,這就是所謂的狀態模式。

狀態模式總結起來一句話:

實體在不同的狀態,擁有不同的行為。

作用是:

可以省掉大量判斷條件帶來的if-else邏輯分支,使得代碼更簡潔易讀。

接下來我們用狀態模式去改寫之前的代碼。

首先總結一下實體類會有的行為有哪些,其實就是付款、發貨、收貨,也就是三個方法,為了代碼的規范,可以抽象出行為接口,當然不抽象也可以,仁者見仁智者見智。

public interface OrderState{void pay(Order order);void ship(Order order);void receive(Order order);
}

接下來總結一下系統里面的狀態,訂單有三個維度的六種狀態,分別是:

  • 付款狀態

    • 未付款

    • 已付款

  • 發貨狀態

    • 未發貨

    • 已發貨

  • 收貨狀態

    • 未收貨

    • 已收貨

于是可以得到有三個狀態實體。

將狀態和行為綁定,可以得到以下三個狀態實體。

支付狀態實體:

public class PayState implements OrderState{public void pay(Order order) {System.out.println("已支付,不能再次支付!");}
?public void ship(Order order) {order.setOrderState(new ShipState());System.out.println("已發貨!");}
?public void receive(Order order) {System.out.println("未發貨!不能收貨!");}
}

發貨狀態實體:

public class ShipState implements OrderState{public void pay(Order order) {System.out.println("已發貨!禁止重復支付!");}
?public void ship(Order order) {System.out.println("已經發貨!禁止重復支付");}
?public void receive(Order order) {order.setOrderState(new ReceiveState());System.out.println("收貨成功!");}
}

收貨狀態實體

public class ReceiveState implements OrderState{public void pay(Order order) {System.out.println("已收貨,不能再次支付!");}
?public void ship(Order order) {System.out.println("已收貨,不能再次發貨!");}
?public void receive(Order order) {System.out.println("已收貨,不能再次收貨!");}
}

測試代碼:

public class Test {public static void main(String[] args) {Order order=new Order();//初始狀態未待支付order.setOrderState(new PayState());order.pay();order.ship();order.receive();}
}

測試結果:

2.電梯業務場景

2.1.需求

我們考慮一個簡單的電梯系統,其中有以下狀態:

  1. 停止狀態(StoppedState): 當電梯處于停止狀態時,它可以接受移動到指定樓層的請求。

  2. 上升狀態(MovingState): 當電梯處于上升狀態時,它不能響應移動請求,因為它正在上升。

  3. 下降狀態(MovingState): 當電梯處于下降狀態時,它也不能響應移動請求,因為它正在下降。

規則:

  • 當電梯處于停止狀態時,它可以接受移動到指定樓層的請求,并切換到移動狀態(上升或下降)。

  • 當電梯處于上升狀態或下降狀態時,它不能接受移動請求,而是提示當前正在移動。

  • 電梯在移動過程中不能響應其他移動請求,直到它到達指定樓層并切換到停止狀態。

在上面的業務情景中,我們通過使用狀態模式對電梯系統進行了優化。每個狀態(停止狀態和移動狀態)都對應一個狀態類,并定義了在該狀態下的行為。電梯狀態的切換由上下文類(ElevatorStateContext)來管理,它負責在不同狀態下執行不同的行為,并根據狀態的變化進行切換。通過使用狀態模式,我們將狀態切換邏輯封裝到不同的狀態類中,使代碼更加模塊化和可擴展。

2.2.if else的實現

class ElevatorIfElse {private String state = "停止";private int currentFloor = 1;
?public void setState(String newState) {state = newState;}
?public void moveToFloor(int floor) {if (state.equals("停止")) {System.out.println("電梯從 " + currentFloor + " 樓移動到 " + floor + " 樓");currentFloor = floor;} else if (state.equals("上升")) {System.out.println("電梯正在上升,不能移動");} else if (state.equals("下降")) {System.out.println("電梯正在下降,不能移動");}}
}
?
public class MainIfElse {public static void main(String[] args) {ElevatorIfElse elevator = new ElevatorIfElse();
?elevator.moveToFloor(5);elevator.setState("上升");elevator.moveToFloor(3);elevator.moveToFloor(7);elevator.setState("停止");elevator.moveToFloor(2);}
}

2.3.狀態模式的實現

interface ElevatorState {void moveToFloor(ElevatorStateContext context, int floor);
}
?
class StoppedState implements ElevatorState {@Overridepublic void moveToFloor(ElevatorStateContext context, int floor) {System.out.println("電梯從 " + context.getCurrentFloor() + " 樓移動到 " + floor + " 樓");context.setCurrentFloor(floor);context.setState(new MovingState());}
}
?
class MovingState implements ElevatorState {@Overridepublic void moveToFloor(ElevatorStateContext context, int floor) {System.out.println("電梯正在移動,不能移動");}
}
?
class ElevatorStateContext {private ElevatorState state;private int currentFloor = 1;
?public ElevatorStateContext() {this.state = new StoppedState();}
?public void setState(ElevatorState state) {this.state = state;}
?public void moveToFloor(int floor) {state.moveToFloor(this, floor);}
?public int getCurrentFloor() {return currentFloor;}
?public void setCurrentFloor(int currentFloor) {this.currentFloor = currentFloor;}
}
?
public class MainStatePattern {public static void main(String[] args) {ElevatorStateContext context = new ElevatorStateContext();
?context.moveToFloor(5);context.setState(new MovingState());context.moveToFloor(3);context.moveToFloor(7);context.setState(new StoppedState());context.moveToFloor(2);}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/36546.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/36546.shtml
英文地址,請注明出處:http://en.pswp.cn/news/36546.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Axure高保真原型】通過輸入框動態控制環形圖

今天和大家分享通過輸入框動態控制環形圖的原型模板,在輸入框里維護項目數據,可以自動生成對應的環形圖,鼠標移入對應扇形,可以查看對應數據。使用也非常方便,只需要修改輸入框里的數據,或者復制粘貼文本&a…

簡單記錄牛客top101算法題(初級題C語言實現)BM17 二分查找 BM21 旋轉數組的最小數字 BM23 二叉樹的前序遍歷

1. BM17 二分查找 要求:給定一個 元素升序的、無重復數字的整型數組 nums 和一個目標值 target ,寫一個函數搜索 nums 中的 target,如果目標值存在返回下標(下標從 0 開始),否則返回 -1。 輸入&#xff1a…

【云原生】K8S存儲卷:PV、PVC詳解

目錄 一、emptyDir存儲卷二、hostPath存儲卷三、nfs共享存儲卷四、PVC 和 PV4.1 NFS使用PV和PVC4.2創建動態PV 一、emptyDir存儲卷 容器磁盤上的文件的生命周期是短暫的,這就使得在容器中運行重要應用時會出現一些問題。首先,當容器崩潰時,ku…

UG NX二次開發(C++)-PK函數創建一條圓弧曲線

文章目錄 1、前言2、創建一個項目3、添加頭文件4、在do_it中添加創建圓曲線的源代碼5、調用dll6、再創建一個長方體驗證1、前言 采用PK進行UG NX二次開發,現在看到的文章很多是直接創建實體,然后在UG NX的視圖區顯示出來,對于創建圓曲線的文章不多,本文講一下PK函數創建圓…

Java基礎篇--日期時間類

目錄 前言 Instant(時間戳)類 LocalData(日期)類 LocalTime(時間)類 LocalDataTime(日期時間)類 Duration(時間間隔)類 Period(日期間隔)類 Clock(獲取時區)類 前言 在開發中經常需要處理日期和時間,Java提供…

Git 代碼分支規范

目的 俗話說:沒有規矩,不成方圓。遵循一個好的規章制度能讓你的工作事半功倍。同時也可以展現出你做事的認真的態度以及你的專業性,不會顯得雜亂無章,管理困難。Git分支規范也是一樣。當遵循了某種約定的Git分支,在代…

若依框架淺淺介紹

由若依官網所給介紹可知 1、文件結構介紹 在ruoyi-admin的pom.xml文件中引入了ruoyi-framework、ruoyi-quartz和ruoyi-generatior模塊,在ruoyi-framework的pom.xml文件中引入了ruoyi-system模塊。 2、技術棧介紹 前端:Vue、Element UI后端&#xff1a…

Redis持久化機制簡介

當涉及到Redis的持久化時,有兩種主要的持久化方式:RDB(Redis Database)快照和AOF(Append-Only File)日志。這些方式可以根據需求的不同,選擇適合的策略。 RDB(Redis Database&#…

第1章:緒論

科學、技術、工程、應用 科學:是什么、為什么技術:怎么做工程:怎樣做的多快好省應用:怎么使用 定義 機器學習:利用經驗改善系統自身的性能。 研究 智能數據分析(數據分析算法) 典型的機器…

電腦ip地址怎么改 ip地址怎么改到別的城市

一、ip地址怎么改到別的城市 1.ip地址怎么改到別的城市,1、重啟WIFI路由設備 一般手機或電腦在家或公司上網時都是接入到路由器的WIFI網絡,再由路由器分配上網IP地址,如果要更換上網IP那么重啟路由器設備后,路由器會向網絡運營商進行寬帶的重新撥號,此時手機或電腦設…

【【verilog 典型電路設計之加法器樹乘法器】】

verilog 典型電路設計之加法器樹乘法器 加法器樹乘法器 加法器樹乘法器的設計思想是“移位后加”,并且加法運算采用加法器樹的形式。乘法運算的過程是,被乘數與乘數的每一位相乘并且乘以相應的權值,最后將所得的結果相加,便得到了…

mongodb:環境搭建

mongodb 是什么? MongoDB是一款為web應用程序和互聯網基礎設施設計的數據庫管理系統。沒錯MongoDB就是數據庫,是NoSQL類型的數據庫 為什么要用mongodb? (1)MongoDB提出的是文檔、集合的概念,使用BSON&am…

【Go】常見的四個內存泄漏問題

Goroutine沒有順利結束 1、這里更多的是由于channelforselect導致的,錯誤的寫法導致了發送者或接收者沒有發現channel已經關閉,任務已經結束了,卻仍然在嘗試輸入輸出https://geektutu.com/post/hpg-exit-goroutine.html Map的remove方法不會…

selenium.webdriver Python爬蟲教程

文章目錄 selenium安裝和使用 selenium安裝和使用 pip install selenium 下載對應的瀏覽器驅動 實例化瀏覽器 from selenium import webdriverbrowser webdriver.Chrome()元素定位 控制瀏覽器

HTB-Keeper

HTB-Keeper 信息收集80端口 lnorgaardroot 信息收集 80端口 80主頁給了一個跳轉的鏈接 跟隨鏈接后到了一個登陸界面。 嘗試搜索默認密碼。 通過賬號root:password登錄。不知道為什么我登陸了兩次才成功。 通過搜索在Admin->Users->Select里面發現了用戶信息。 lno…

WS2812B————動/靜態顯示

一,系統架構 二,芯片介紹 1.管腳說明 2.數據傳輸時間 3.時序波形 4.數據傳輸方法 5.常用電路連接 三,代碼展示及說明 驅動模塊 在驅動模塊首先選擇使用狀態機,其中包括,空閑狀態,復位清空狀態&#xff0c…

怎么把圖片表格轉換成word表格?幾個步驟達成

在處理文檔時,圖片表格的轉換是一個常見的需求。而手動輸入表格是非常耗時的,因此,使用文本識別軟件來自動轉換圖片表格可以大大提高工作效率。在本文中,我們將介紹如何使用OCR文字識別技術來將圖片表格轉換為Word表格。 OCR文字識…

Vue3+Element plus+pageHelper實現分頁

安裝element plus npm install element-plus --save引入 修改main.js: import { createApp } from vue import App from ./App.vue import ElementPlus from element-plus import element-plus/dist/index.cssconst app createApp(App) app.use(ElementPlus) ap…

15.3 【Linux】循環執行的例行性工作調度

相對于 at 是僅執行一次的工作,循環執行的例行性工作調度則是由 cron (crond) 這個系統服務來控制的。剛剛談過 Linux 系統上面原本就有非常多的例行性工作,因此這個系統服務是默認啟動的。另外, 由于使用者自己也可以…

棧和隊列--受限制的線性表

目錄 和隊列的定義和特點 1.1棧的定義和特點、 1.2隊列的定義和特點 1.3棧和隊列的應用 2.棧的表示和操作的實現 2.1棧的類型定義 2.2順序棧的表示和實現 2.2.1初始化 2.2.2入棧 2.2.3出棧 2.2.4取棧頂元素 2.3鏈棧的表示和實現 2.2.1初始化 2.2.2入棧 2.2.3出棧…