狀態模式是一種通過封裝狀態行為來實現狀態切換的面向對象設計方式。1. 它將每個狀態定義為獨立類,使狀態變化驅動行為改變,從而提升代碼可維護性與擴展性;2. 通過上下文對象(如door)持有當前狀態并委托請求,避免了冗長條件判斷;3. 狀態轉換在具體狀態類中處理,新增狀態無需修改已有邏輯;4. 相比策略模式,其側重內部狀態而非外部算法選擇;5. 在游戲開發中廣泛用于角色狀態管理;6. 多線程環境下需結合互斥鎖確保狀態切換安全。
狀態機,簡單來說,就是在不同條件下切換自身狀態的機制。在c++里,實現狀態機的方法有很多,可以用switch-case,也可以用函數指針,但更優雅、更面向對象的方式,通常會選擇狀態模式。狀態模式允許對象在內部狀態改變時改變它的行為,看起來好像修改了它的類。
解決方案
狀態模式的核心在于將狀態封裝成獨立的類,然后讓上下文對象持有當前狀態的引用。當上下文對象需要根據狀態執行不同的行為時,它會將請求委托給當前狀態對象。這樣,我們就可以在不修改上下文對象代碼的情況下,通過增加新的狀態類來擴展狀態機的行為。
立即學習“C++免費學習筆記(深入)”;
一個簡單的例子:假設我們有一個門,它可以處于打開、關閉、鎖定三種狀態。
- 定義狀態接口:
class DoorState { public: virtual void open(class Door* door) {} virtual void close(class Door* door) {} virtual void lock(class Door* door) {} virtual void unlock(class Door* door) {} virtual ~DoorState() {} // 確保基類有虛析構函數 };
- 實現具體狀態類:
#include <iostream> // Forward declaration of Door class class DoorState { public: virtual void open(class Door* door); virtual void close(class Door* door); virtual void lock(class Door* door); virtual void unlock(class Door* door); virtual ~DoorState() {} // Ensure base class has virtual destructor }; class OpenState : public DoorState { public: void close(Door* door) override; void lock(Door* door) override; }; class ClosedState : public DoorState { public: void open(Door* door) override; void lock(Door* door) override; }; class LockedState : public DoorState { public: void unlock(Door* door) override; }; class Door { public: DoorState* state; Door(DoorState* initialState) : state(initialState) {} void setState(DoorState* newState) { delete state; // Important: Prevent memory leaks state = newState; } void open() { state->open(this); } void close() { state->close(this); } void lock() { state->lock(this); } void unlock() { state->unlock(this); } ~Door() { delete state; // Clean up the state object } }; void OpenState::close(Door* door) { std::cout << "Closing the door.n"; door->setState(new ClosedState()); } void OpenState::lock(Door* door) { std::cout << "Cannot lock an open door.n"; } void ClosedState::open(Door* door) { std::cout << "Opening the door.n"; door->setState(new OpenState()); } void ClosedState::lock(Door* door) { std::cout << "Locking the door.n"; door->setState(new LockedState()); } void LockedState::unlock(Door* door) { std::cout << "Unlocking the door.n"; door->setState(new ClosedState()); } int main() { Door* door = new Door(new ClosedState()); // Start with the door closed door->open(); door->close(); door->lock(); door->unlock(); //door->unlock(); // Example of unlocking when already unlocked - no transition delete door; // Clean up the Door object return 0; }
- 上下文對象(Door):
Door類持有當前狀態的指針,并提供open()、close()、lock()、unlock()方法,這些方法會將請求委托給當前狀態對象。
狀態模式的優點在于,它使得狀態的切換和行為的改變更加清晰和可維護。每種狀態都封裝在自己的類中,易于理解和修改。而且,增加新的狀態也很容易,只需要創建新的狀態類即可,不需要修改現有的代碼。
狀態模式的缺點是,如果狀態很多,類的數量也會很多,可能會增加代碼的復雜性。另外,狀態之間的轉換可能會比較復雜,需要仔細設計。
狀態模式與策略模式的區別?
狀態模式和策略模式都是處理對象行為變化的模式,但它們的應用場景有所不同。狀態模式主要關注對象內部狀態的變化,以及狀態變化引起的行為改變。而策略模式則關注算法的選擇,允許客戶端在運行時選擇不同的算法。
簡單來說,狀態模式是對象自身狀態驅動行為的改變,策略模式是客戶端選擇不同的算法來完成任務。
狀態模式在游戲開發中的應用?
在游戲開發中,狀態模式應用廣泛。例如,一個角色可以有多種狀態,如Idle、Walking、Running、Jumping、Attacking等。每種狀態對應不同的行為。使用狀態模式可以清晰地管理角色的狀態和行為,使得代碼更加模塊化和易于維護。
比如,角色在Attacking狀態下,不能執行Jumping操作,或者Running狀態下,可以切換到Attacking狀態。這些狀態之間的轉換和行為的改變,都可以通過狀態模式來實現。
狀態模式的線程安全性問題?
如果多個線程同時訪問和修改狀態機的狀態,可能會出現線程安全問題。為了保證線程安全,可以使用互斥鎖(Mutex)或其他同步機制來保護狀態的訪問和修改。
例如,可以在Door類的setState()方法中使用互斥鎖來防止多個線程同時修改狀態:
#include <mutex> class Door { public: DoorState* state; std::mutex mtx; // 添加互斥鎖 Door(DoorState* initialState) : state(initialState) {} void setState(DoorState* newState) { std::lock_guard<std::mutex> lock(mtx); // 加鎖 delete state; state = newState; } // ... 其他方法 ... };
這樣,在修改狀態之前,會先獲取互斥鎖,防止其他線程同時修改狀態,從而保證線程安全。當然,在狀態類的各個方法中,如果涉及到共享數據的訪問和修改,也需要使用互斥鎖或其他同步機制來保護。