状态模式 - Flutter中的状态变身术,让对象随“状态“自由切换行为!

发布于:2025-07-01 ⋅ 阅读:(18) ⋅ 点赞:(0)

订单状态流转/播放器控制/游戏角色行为…一个模式搞定所有状态驱动型逻辑!


经典场景:订单状态管理

假设你在开发一个外卖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;
      // ...其他状态
    }
  }
}

痛点:

  • 代码臃肿,一个类包含所有状态的逻辑
  • 添加新状态需要修改现有类
  • 状态转换逻辑分散在各处
  • 违反开闭原则(对扩展开放,对修改关闭)

状态模式解决方案

核心思想: 允许对象在其内部状态改变时改变它的行为,对象看起来像是修改了它的类。

三个关键角色:

  1. 上下文(Context): 维护当前状态,定义状态接口
  2. 抽象状态(State): 定义状态接口
  3. 具体状态(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();

状态模式最佳实践

  1. 何时使用状态模式:

    • 对象的行为取决于它的状态
    • 状态数量较多(>3)
    • 状态转换逻辑复杂
    • 需要避免使用大量的条件语句
  2. 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 => // 从当前状态推断类型
    }
    
  3. 状态转换管理:

    // 状态转换表
    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));
      }
    }
    
  4. 状态持久化:

    // 保存状态到本地
    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状态管理

🎮 设计启示: 当你的对象需要根据状态改变行为,且状态转换逻辑复杂时,状态模式是优雅的解决方案!


网站公告

今日签到

点亮在社区的每一天
去签到