C++設計模式:狀態模式(自動售貨機)

什么是狀態模式?

狀態模式是一種行為型設計模式,它允許一個對象在其內部狀態發生改變時,動態改變其行為。通過將狀態相關的邏輯封裝到獨立的類中,狀態模式能夠將狀態管理與行為解耦,從而讓系統更加靈活和可維護。

通俗理解

想象一個自動售貨機,它有以下幾種狀態:

  1. 無硬幣狀態:等待用戶投入硬幣。
  2. 有硬幣狀態:用戶可以選擇商品。
  3. 售貨狀態:用戶選擇商品后,售貨機正在出貨。
  4. 缺貨狀態:商品已售罄。

售貨機的行為完全依賴于當前的狀態,比如:

  • 無硬幣狀態下,用戶無法選擇商品。
  • 有硬幣狀態下,用戶可以選擇商品。
  • 售貨狀態下,售貨機執行出貨操作。

狀態模式的核心就是:將狀態邏輯抽象為獨立的狀態類,并通過上下文類(如售貨機)動態切換狀態對象,進而改變對象的行為。


狀態模式的特點

優點
  1. 清晰封裝狀態邏輯

    • 每種狀態的邏輯被集中到對應的狀態類中,邏輯清晰且易于管理。
  2. 動態切換行為

    • 對象的行為可以通過切換狀態動態改變,無需修改上下文類的代碼。
  3. 擴展性強

    • 新增狀態只需添加新的狀態類,不影響現有代碼,符合開閉原則
缺點
  1. 類的數量增加

    • 每種狀態都需要一個單獨的類,可能導致類數量較多。
  2. 狀態切換邏輯分散

    • 狀態之間的切換邏輯分布在多個狀態類中,增加了維護的復雜性。

狀態模式的結構

UML類圖
         +---------------------+|       Context       |   // 上下文類,管理當前狀態+---------------------+|  - state: State     ||  + setState(state)  ||  + request()        |+---------------------+^|+---------------------+|       State         |   // 抽象狀態類+---------------------+|  + handle()         |+---------------------+^|+----------------------+  +----------------------+|  ConcreteStateA      |  |  ConcreteStateB      |   // 具體狀態類+----------------------+  +----------------------+|  + handle()          |  |  + handle()          |+----------------------+  +----------------------+
組成部分
  1. State(抽象狀態類)

    • 定義了所有狀態的通用接口,例如insertCoin()selectProduct()等。
  2. ConcreteState(具體狀態類)

    • 實現State接口,定義與特定狀態相關的行為。
  3. Context(上下文類)

    • 持有一個狀態對象,調用當前狀態的行為。
    • 負責在運行時切換狀態對象。

案例:自動售貨機

需求描述

設計一個簡單的自動售貨機,支持以下功能:

  1. 無硬幣狀態:用戶不能選擇商品。
  2. 有硬幣狀態:用戶可以選擇商品。
  3. 售貨狀態:用戶選擇商品后,售貨機出貨。
目標
  • 實現售貨機的狀態管理,使得行為隨著狀態變化而動態改變。
  • 狀態切換應簡單且易于擴展。

完整代碼實現

以下是狀態模式在自動售貨機中的應用,輸出為中文,附帶詳細注釋。

#include <iostream>
#include <memory>
#include <string>// 抽象狀態類
class State {
public:virtual void insertCoin() = 0;      // 投入硬幣virtual void selectProduct() = 0;  // 選擇商品virtual void dispenseProduct() = 0; // 出貨virtual ~State() = default;
};// 前向聲明:解決狀態類互相引用的問題
class VendingMachine;
class HasCoinState;// 上下文類:自動售貨機
class VendingMachine {
private:std::shared_ptr<State> currentState; // 當前狀態public:void setState(std::shared_ptr<State> state) {currentState = state; // 切換狀態}// 調用當前狀態的方法void insertCoin() {currentState->insertCoin();}void selectProduct() {currentState->selectProduct();}void dispenseProduct() {currentState->dispenseProduct();}
};// 具體狀態類:無硬幣狀態
class NoCoinState : public State {
private:VendingMachine* machine; // 上下文public:explicit NoCoinState(VendingMachine* machine) : machine(machine) {}void insertCoin() override {std::cout << "硬幣已投入,進入有硬幣狀態。" << std::endl;machine->setState(std::static_pointer_cast<State>(std::make_shared<HasCoinState>(machine))); // 切換到有硬幣狀態}void selectProduct() override {std::cout << "請先投入硬幣。" << std::endl;}void dispenseProduct() override {std::cout << "請先投入硬幣并選擇商品。" << std::endl;}
};// 具體狀態類:有硬幣狀態
class HasCoinState : public State {
private:VendingMachine* machine;public:explicit HasCoinState(VendingMachine* machine) : machine(machine) {}void insertCoin() override {std::cout << "硬幣已存在,請選擇商品。" << std::endl;}void selectProduct() override {std::cout << "商品已選擇,正在出貨。" << std::endl;machine->setState(std::static_pointer_cast<State>(std::make_shared<NoCoinState>(machine))); // 模擬出貨后切換到無硬幣狀態}void dispenseProduct() override {std::cout << "請先選擇商品。" << std::endl;}
};// 客戶端代碼
int main() {// 創建自動售貨機并設置初始狀態VendingMachine machine;machine.setState(std::make_shared<NoCoinState>(&machine));// 測試售貨機的功能std::cout << "=== 測試場景 1:無硬幣狀態 ===" << std::endl;machine.selectProduct(); // 未投硬幣時選擇商品machine.insertCoin();    // 投入硬幣std::cout << "\n=== 測試場景 2:有硬幣狀態 ===" << std::endl;machine.selectProduct(); // 投幣后選擇商品return 0;
}

運行結果

=== 測試場景 1:無硬幣狀態 ===
請先投入硬幣。
硬幣已投入,進入有硬幣狀態。=== 測試場景 2:有硬幣狀態 ===
商品已選擇,正在出貨。

代碼解讀

1. 抽象狀態類(State
class State {
public:virtual void insertCoin() = 0;virtual void selectProduct() = 0;virtual void dispenseProduct() = 0;virtual ~State() = default;
};
  • 定義了所有狀態的接口。
  • 子類實現這些方法來處理具體的狀態邏輯。
2. 上下文類(VendingMachine
class VendingMachine {
private:std::shared_ptr<State> currentState;public:void setState(std::shared_ptr<State> state) {currentState = state;}void insertCoin() {currentState->insertCoin();}void selectProduct() {currentState->selectProduct();}void dispenseProduct() {currentState->dispenseProduct();}
};
  • 持有當前狀態對象,并將行為委托給當前狀態。
  • 通過setState方法切換狀態。
3. 具體狀態類
  • 無硬幣狀態
void insertCoin() override {std::cout << "硬幣已投入,進入有硬幣狀態。" << std::endl;machine->setState(std::static_pointer_cast<State>(std::make_shared<HasCoinState>(machine)));
}
  • 有硬幣狀態
void selectProduct() override {std::cout << "商品已選擇,正在出貨。" << std::endl;machine->setState(std::static_pointer_cast<State>(std::make_shared<NoCoinState>(machine)));
}

每個具體狀態實現了當前狀態的行為邏輯,同時可以切換到其他狀態。


狀態模式的應用場景

  1. 對象行為取決于狀態

    • 自動售貨機、訂單狀態管理、游戲角色狀態(如站立、跑動、跳躍等)。
  2. 需要消除復雜條件判斷

    • 替代if-elseswitch語句,將狀態邏輯分散到獨立的狀態類中。

總結

狀態模式是一種非常實用的設計模式,它通過將狀態邏輯封裝到獨立的類中,動態改變對象的行為,從而提高代碼的擴展性和可維護性。

核心優勢
  1. 行為動態改變:通過切換狀態對象,動態改變對象的行為。
  2. 易擴展:新增狀態只需添加狀態類,不影響現有代碼。
  3. 邏輯清晰:每個狀態的邏輯集中在對應的狀態類中,代碼更易于理解和維護。
典型應用
  • 自動售貨機
  • 在線訂單狀態(待支付、已支付、已發貨等)
  • 游戲角色狀態(站立、奔跑、跳躍等)

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

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

相關文章

【Pytorch實用教程】循環神經網絡中使用dropout需要注意的問題

文章目錄 問題解答警告的具體含義解決方案示例代碼總結問題 UserWarning: dropout option adds dropout after all but last recurrent layer, so non-zero dropout expects num_layers greater than 1, but got dropout=0.3 and num_layers=1 warnings.warn("dropout op…

數據中臺與數據治理服務方案[50頁PPT]

本文概述了數據中臺與數據治理服務方案的核心要點。數據中臺作為政務服務數據化的核心&#xff0c;通過整合各部門業務系統數據&#xff0c;進行建模與加工&#xff0c;以新數據驅動政府管理效率提升與政務服務能力增強。數據治理則聚焦于解決整體架構問題&#xff0c;確保數據…

postgresq-自定義執行計劃(custom plan)與generic plan(通用執行計劃)

文章目錄 之前寫過一篇關于 PostgreSQL prepare sql的文章&#xff0c;但當時沒有提到generic plan(通用計劃)和custom plan(自定義計劃)這兩個概念。現在將通過舉例介紹這兩個概念。 創建測試表&#xff1a; postgres# create database demo; CREATE DATABASE postgres# \c d…

dockfile 配置 /etc/apt/source.list.d/debian.list 清華鏡像

docker:3.12.7 鏡像使用的是 debian 系統&#xff0c;比 ubuntu 更輕量。debian 系統內&#xff0c;apt 鏡像源列表位于 /etc/apt/source.list.d/debian.list&#xff08;作為對比&#xff0c;ubuntu 的鏡像列表位于 /etc/apt/source.list&#xff0c;二者語法相同&#xff09;…

程序員測試日常小工具

作為一名程序員&#xff0c;或者測試人員&#xff0c;日常工作最常用的工具有哪些&#xff0c;截圖&#xff0c;截圖漂浮&#xff0c;翻譯&#xff0c;日期處理&#xff0c;api調用...&#xff0c; 當你拿到一串報文后&#xff0c;想要json轉換時&#xff0c;是不是要打…

【MySQL高級】第1-4章

第1章 存儲過程 1.1 什么是存儲過程&#xff1f; 存儲過程可稱為過程化SQL語言&#xff0c;是在普通SQL語句的基礎上增加了編程語言的特點&#xff0c;把數據操作語句(DML)和查詢語句(DQL)組織在過程化代碼中&#xff0c;通過邏輯判斷、循環等操作實現復雜計算的程序語言。 換…

深入淺出:AWT事件監聽器及其應用

前言 在Java的GUI編程中&#xff0c;事件處理是非常重要的一環。AWT&#xff08;Abstract Window Toolkit&#xff09;框架提供了靈活的事件處理機制&#xff0c;使得開發者能夠響應用戶的操作&#xff0c;例如點擊按鈕、鍵盤輸入、鼠標點擊等。AWT的事件監聽器就是實現這一機…

【Rust自學】8.5. HashMap Pt.1:HashMap的定義、創建、合并與訪問

8.5.0. 本章內容 第八章主要講的是Rust中常見的集合。Rust中提供了很多集合類型的數據結構&#xff0c;這些集合可以包含很多值。但是第八章所講的集合與數組和元組有所不同。 第八章中的集合是存儲在堆內存上而非棧內存上的&#xff0c;這也意味著這些集合的數據大小無需在編…

混合合并兩個pdf文件

混合兩個pdf 1、在線免費交替和混合奇數和偶數PDF頁面2、有什么軟件把兩個 PDF 交叉合并&#xff1f;3、pdfsam本地合并 如何Google翻譯的原文和譯文合并&#xff0c;&#xff08;沉浸式翻譯效果相對較好&#xff09; 1、在線免費交替和混合奇數和偶數PDF頁面 https://deftpd…

Hutool 發送 HTTP 請求的幾種常見寫法

最簡單的 GET 請求&#xff1a; String result HttpUtil.get("https://www.baidu.com");帶參數的 GET 請求&#xff1a; // 方法1: 直接拼接URL參數 String result HttpUtil.get("https://www.baidu.com?name張三&age18");// 方法2: 使用 HashMap…

獲取用戶詳細信息-ThreadLocal優化

Thread全局接口可用&#xff0c;不用再重復編寫。所以為了代碼的復用&#xff0c;使用Thread。把之前的內容&#xff08;函數的參數和map與username&#xff09;注釋掉&#xff0c;換為Thread傳過來的內容&#xff08;map與username&#xff09;。 因為Thread需要在攔截器里面…

THUCNews解壓/THUCNews數據集解壓出問題

省流&#xff1a;使用zip64進行解壓&#xff0c;文件數目太多windows默認zip16裝不下 我在使用THUCNews中文文本數據集時出現了問題&#xff0c;原數據集解壓后應該包含以下兩個文件夾: 其中THUCNews文件夾下有以新聞類別命名的子文件。官網下載的是一個1.56GB的zip壓縮包 而我…

MySQL使用通用二進制文件安裝到Unix/Linux

Oracle提供了一組MySQL的二進制發行版。其中包括用于許多平臺的壓縮tar文件&#xff08;擴展名為.tar.xz的文件&#xff09;形式的通用二進制發行版&#xff0c;以及用于選定平臺的特定平臺包格式的二進制文件。 本節介紹在Unix/Linux平臺上從壓縮的tar文件二進制分布安裝MySQ…

安卓/system/bin下命令中文說明(AI)

ATFWD-daemon&#xff1a;AT指令轉發守護進程&#xff0c;用于將AT指令從應用層轉發到調制解調器。 PktRspTest&#xff1a;數據包響應測試工具。 StoreKeybox&#xff1a;存儲密鑰盒工具&#xff0c;用于安全地存儲加密密鑰。 WifiLogger_app&#xff1a;WiFi日志記錄應用&…

Git操作總結

可以直接看實踐 總結自施磊老師課程 Git與SVN對比 svn操作流程 寫代碼。 從服務器拉回服務器的當前版本庫&#xff0c;并解決服務器版本庫與本地代碼的沖突。 將本地代碼提交到服務器。 Git操作流程 寫代碼&#xff0c; 然后添加&#xff08;add&#xff09;到暫存區。 …

直流開關電源技術及應用二

文章目錄 8 PFC8.1 基本概念8.1.1 功率因數8.1.2 功率因數偏低帶來的影響8.1.3 特點 8.2 有源功率因數校正原理8.2.1不連續工作模式的矯正原理恒頻控制技術控制目標控制關鍵要素控制過程實現方式公式Boost電路和boost pfc電路的聯系和區別聯系區別 恒導通時間控制 8.2.2 連續工…

UNI-APP_i18n國際化引入

官方文檔&#xff1a;https://uniapp.dcloud.net.cn/tutorial/i18n.html vue2中使用 1. 新建文件 locale/index.js import en from ./en.json import zhHans from ./zh-Hans.json import zhHant from ./zh-Hant.json const messages {en,zh-Hans: zhHans,zh-Hant: zhHant }…

typora+picgo core+minio自動上傳圖片

1. 在服務器上安裝docker版本minio 創建/docker/minio文件夾 mkdir -p /docker/minio在此文件夾創建docker-compose.yml version: "3.5" services:minio:image: quay.io/minio/minio:latestcontainer_name: minioprivileged: truerestart: alwaysports:# API接口訪…

論文筆記:DepthLab: From Partial to Complete

是一篇很精煉的論文&#xff0c;不知道咋總結了&#xff0c;就差全文翻譯了&#xff0c;不過在這里我主要關注3D部分&#xff0c;因為他的pipeline是基于SD的&#xff0c;框圖也比較清晰易懂&#xff0c;非常細節的內容可以回頭看論文&#xff0c;哈哈哈&#xff0c;給作者大佬…

LeetCode--排序算法(堆排序、歸并排序、快速排序)

排序算法 歸并排序算法思路代碼時間復雜度 堆排序什么是堆&#xff1f;如何維護堆&#xff1f;如何建堆&#xff1f;堆排序時間復雜度 快速排序算法思想代碼時間復雜度 歸并排序 算法思路 歸并排序算法有兩個基本的操作&#xff0c;一個是分&#xff0c;也就是把原數組劃分成…