訂單狀態流轉/播放器控制/游戲角色行為…一個模式搞定所有狀態驅動型邏輯!
經典場景:訂單狀態管理
假設你在開發一個外賣App,訂單有以下狀態:
等待接單
已接單
配送中
已完成
已取消
每個狀態下:
- 顯示的UI不同
- 可執行的操作不同
- 狀態轉換規則不同
傳統實現方式(switch-case地獄):
class Order {String state = 'waiting'; // 狀態字段Widget buildUI() {switch(state) {case 'waiting':return _buildWaitingUI();case 'accepted':return _buildAcceptedUI();// ...其他狀態}}void performAction(String action) {switch(state) {case 'waiting':if (action == 'accept') {state = 'accepted';// 執行接單邏輯}break;case 'accepted':if (action == 'pickup') {state = 'delivering';// 執行取貨邏輯}break;// ...其他狀態}}
}
痛點:
- 代碼臃腫,一個類包含所有狀態的邏輯
- 添加新狀態需要修改現有類
- 狀態轉換邏輯分散在各處
- 違反開閉原則(對擴展開放,對修改關閉)
狀態模式解決方案
核心思想: 允許對象在其內部狀態改變時改變它的行為,對象看起來像是修改了它的類。
三個關鍵角色:
- 上下文(Context): 維護當前狀態,定義狀態接口
- 抽象狀態(State): 定義狀態接口
- 具體狀態(ConcreteState): 實現特定狀態的行為
Flutter訂單狀態模式實現
1. 定義狀態接口
abstract class OrderState {Widget buildUI(OrderContext context);void acceptOrder(OrderContext context);void pickUp(OrderContext context);void deliver(OrderContext context);void complete(OrderContext context);void cancel(OrderContext context);
}
2. 實現具體狀態類
// 等待接單狀態
class WaitingState implements OrderState { Widget buildUI(OrderContext context) {return Column(children: [Text('等待商家接單...', style: TextStyle(color: Colors.orange)),ElevatedButton(onPressed: () => context.acceptOrder(),child: Text('接單'),)],);}void acceptOrder(OrderContext context) {print('訂單已接單');context.changeState(AcceptedState());}// 其他方法在當前狀態下不適用void pickUp(OrderContext context) => _showError('接單后才能取貨');void deliver(OrderContext context) => _showError('請先取貨');void complete(OrderContext context) => _showError('訂單未完成');void cancel(OrderContext context) {print('訂單已取消');context.changeState(CanceledState());}void _showError(String msg) => print('操作失敗: $msg');
}// 已接單狀態
class AcceptedState implements OrderState { Widget buildUI(OrderContext context) {return Column(children: [Text('商家已接單', style: TextStyle(color: Colors.green)),ElevatedButton(onPressed: () => context.pickUp(),child: Text('取貨'),)],);}void pickUp(OrderContext context) {print('商品已取貨');context.changeState(DeliveringState());}// ...其他方法實現類似
}// 其他狀態類:DeliveringState, CompletedState, CanceledState
3. 創建上下文類
class OrderContext {OrderState _state = WaitingState();void changeState(OrderState newState) {_state = newState;}// 委托所有操作給當前狀態Widget buildUI() => _state.buildUI(this);void acceptOrder() => _state.acceptOrder(this);void pickUp() => _state.pickUp(this);void deliver() => _state.deliver(this);void complete() => _state.complete(this);void cancel() => _state.cancel(this);
}
4. 在Flutter組件中使用
class OrderScreen extends StatefulWidget { _OrderScreenState createState() => _OrderScreenState();
}class _OrderScreenState extends State<OrderScreen> {final OrderContext orderContext = OrderContext(); Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('訂單詳情')),body: Center(child: orderContext.buildUI(),),floatingActionButton: FloatingActionButton(onPressed: () => orderContext.cancel(),tooltip: '取消訂單',child: Icon(Icons.cancel),),);}
}
Flutter中的實際應用場景
場景1:媒體播放器控制
// 狀態接口
abstract class PlayerState {void play(PlayerContext context);void pause(PlayerContext context);void stop(PlayerContext context);IconData get icon;
}// 具體狀態
class PlayingState implements PlayerState { void play(PlayerContext context) => print('已在播放中'); void pause(PlayerContext context) => context.changeState(PausedState()); void stop(PlayerContext context) => context.changeState(StoppedState()); IconData get icon => Icons.pause;
}class PausedState implements PlayerState { void play(PlayerContext context) => context.changeState(PlayingState()); void pause(PlayerContext context) => print('已暫停'); void stop(PlayerContext context) => context.changeState(StoppedState()); IconData get icon => Icons.play_arrow;
}// 上下文
class PlayerContext {PlayerState _state = StoppedState();void changeState(PlayerState state) => _state = state;IconData get icon => _state.icon;void play() => _state.play(this);void pause() => _state.pause(this);void stop() => _state.stop(this);
}// 在UI中使用
IconButton(icon: Icon(playerContext.icon),onPressed: () {setState(() {if (playerContext is PlayingState) {playerContext.pause();} else {playerContext.play();}});},
)
場景2:游戲角色狀態
// 游戲角色狀態
abstract class CharacterState {void move();void attack();void takeDamage();String get animation;
}// 具體狀態
class IdleState implements CharacterState { void move() => print('開始移動'); void attack() => print('發起攻擊'); void takeDamage() => print('受到傷害'); String get animation => 'idle_anim';
}class WalkingState implements CharacterState { void move() => print('繼續移動'); void attack() => print('移動中攻擊'); void takeDamage() => print('移動中受傷'); String get animation => 'walk_anim';
}// 上下文
class GameCharacter {CharacterState _state = IdleState();void changeState(CharacterState state) => _state = state;void update() {// 游戲循環中更新狀態render(_state.animation);}void onMoveCommand() => _state.move();void onAttackCommand() => _state.attack();void onDamage() => _state.takeDamage();
}
場景3:表單驗證狀態
abstract class FormState {bool validate();void submit();Color get buttonColor;
}class InvalidState implements FormState { bool validate() => false; void submit() => print('表單無效,不能提交'); Color get buttonColor => Colors.grey;
}class ValidState implements FormState { bool validate() => true; void submit() => print('提交表單'); Color get buttonColor => Colors.blue;
}class FormContext {FormState _state = InvalidState();void validateForm(List<String> errors) {_state = errors.isEmpty ? ValidState() : InvalidState();}void submit() => _state.submit();Color get buttonColor => _state.buttonColor;
}
狀態模式與Flutter狀態管理的結合
將狀態模式與Provider結合使用:
// 創建狀態提供者
class OrderStateProvider extends ChangeNotifier {OrderContext _orderContext = OrderContext();OrderContext get orderContext => _orderContext;void acceptOrder() {_orderContext.acceptOrder();notifyListeners();}void pickUp() {_orderContext.pickUp();notifyListeners();}// 其他操作...
}// 在頂層注冊
void main() {runApp(ChangeNotifierProvider(create: (context) => OrderStateProvider(),child: MyApp(),),);
}// 在組件中使用
Consumer<OrderStateProvider>(builder: (context, provider, child) {return provider.orderContext.buildUI();}
)// 執行操作
context.read<OrderStateProvider>().acceptOrder();
狀態模式最佳實踐
-
何時使用狀態模式:
- 對象的行為取決于它的狀態
- 狀態數量較多(>3)
- 狀態轉換邏輯復雜
- 需要避免使用大量的條件語句
-
Flutter特化技巧:
// 使用枚舉定義狀態類型 enum OrderStateType { waiting, accepted, delivering, completed, canceled }// 狀態工廠 OrderState createState(OrderStateType type) {switch(type) {case OrderStateType.waiting: return WaitingState();case OrderStateType.accepted: return AcceptedState();// ...} }// 在上下文中保存狀態類型 class OrderContext {OrderStateType get stateType => // 從當前狀態推斷類型 }
-
狀態轉換管理:
// 狀態轉換表 const Map<OrderStateType, Map<String, OrderStateType>> transitions = {OrderStateType.waiting: {'accept': OrderStateType.accepted,'cancel': OrderStateType.canceled,},OrderStateType.accepted: {'pickup': OrderStateType.delivering,'cancel': OrderStateType.canceled,},// ... };// 在上下文中使用 void transition(String action) {final nextStateType = transitions[stateType]?[action];if (nextStateType != null) {changeState(createState(nextStateType));} }
-
狀態持久化:
// 保存狀態到本地 void saveState() {SharedPreferences.getInstance().then((prefs) {prefs.setString('order_state', _stateType.toString());}); }// 恢復狀態 void restoreState() {SharedPreferences.getInstance().then((prefs) {final stateStr = prefs.getString('order_state');if (stateStr != null) {final stateType = OrderStateType.values.firstWhere((e) => e.toString() == stateStr);changeState(createState(stateType));}}); }
狀態模式 vs 策略模式
特性 | 狀態模式 | 策略模式 |
---|---|---|
目的 | 管理狀態轉換和狀態相關行為 | 封裝可互換的算法 |
狀態知曉 | 狀態知道其他狀態 | 策略相互獨立 |
狀態改變 | 狀態可改變上下文的狀態 | 策略通常不改變上下文 |
典型應用 | 訂單狀態、播放器控制 | 支付策略、排序算法 |
狀態模式的高級變體
1. 分層狀態機
// 基礎狀態
abstract class BaseState {void handleEvent(Event event);
}// 具體狀態可以包含子狀態
class DeliveryState implements BaseState {BaseState _currentSubState = PreparingState();void handleEvent(Event event) {_currentSubState.handleEvent(event);// 處理狀態轉換if (event is PreparedEvent) {_currentSubState = OnTheWayState();}}
}// 子狀態
class PreparingState implements BaseState {void handleEvent(Event event) {if (event is PrepareCommand) {// 處理準備命令}}
}
2. 歷史狀態
class OrderContext {final List<OrderState> _stateHistory = [];OrderState _currentState = WaitingState();void changeState(OrderState newState) {_stateHistory.add(_currentState);_currentState = newState;}void undo() {if (_stateHistory.isNotEmpty) {_currentState = _stateHistory.removeLast();}}
}
總結:狀態模式是你的行為變身器
- 核心價值: 將不同狀態的行為局部化到各自類中
- Flutter優勢:
- 消除龐大的條件語句
- 簡化狀態轉換邏輯
- 符合單一職責原則
- 提高代碼可擴展性和可維護性
- 適用場景: 訂單系統、游戲角色、媒體播放器、工作流引擎、UI狀態管理
🎮 設計啟示: 當你的對象需要根據狀態改變行為,且狀態轉換邏輯復雜時,狀態模式是優雅的解決方案!