Flutter开发实战:状态模式(State Pattern

状态模式 (State Pattern) 是一种设计模式,用于对象的行为受其状态影响,且其状态可以在运行时动态改变的情况。

核心思想:将特定的状态相关的行为都放入一个对象中,由于每个状态都有对应的行为,所以这导致了大量的状态对象。但当对象的状态转换为其他状态时,对象的行为也会随之改变。

组成部分:

  1. State (状态接口):定义了所有具体状态共享的公共接口。
  2. ConcreteState (具体状态):这些类实现了 State 接口,每一个类代表一个具体的状态,并实现与该状态相关的行为。
  3. Context (上下文):持有一个状态对象的引用,该引用指向当前的状态。外部的请求将被委派给当前的状态对象来处理。

state.svg

优点:

  1. 局部化特定的行为和状态的转换:每一个状态都有其对应的行为,并且状态转换都是明确的。
  2. 状态转换明确:通过将每个状态的行为封装在一个类中,使得查看系统中的状态转换变得非常容易。
  3. 更好的分隔责任:每个状态都有自己的行为,与其他状态分开,避免了大量的条件语句。
  4. 提高代码的可维护性和灵活性:新的状态可以很容易地加入系统中。

缺点:

  1. 可能会导致类的数量增加:系统需要为每一个状态定义一个具体的子类。
  2. 逻辑可能会分散在多个状态类中:当一个行为跨越多个状态类时,可能需要在多个状态类之间进行一些协调。

使用场景:

  • 当一个对象的行为根据其状态而改变,并且它可以在运行时根据当前状态动态改变其行为。
  • 当一个类的操作有大量的分支,且这些分支依赖于该对象的状态。此时状态通常由一个或多个枚举值表示。而每一分支代表一个状态的行为。

状态模式允许一个对象基于其内部状态而有不同的行为,并且可以在运行时透明地转换为另一个状态。

在Flutter中,状态管理是一个核心的概念。实际上Flutter的 StatefulWidgetState 本身就是状态模式的体现。我们可以更进一步,为更复杂的状态管理场景使用状态模式来处理复杂的业务逻辑。

场景一:音量控制

开发一个音乐应用,其中有一个音量控制部分。可以将音量设置为静音、低音量或高音量。在这种情况下,状态模式可以帮助我们更好地管理音量状态和相关行为。

1. State (状态接口)

abstract class VolumeState {
  void volumeUp(VolumeContext context);
  void volumeDown(VolumeContext context);
  String getVolumeStatus();
}

2. ConcreteState (具体状态)

class MutedVolume implements VolumeState {
  @override
  void volumeUp(VolumeContext context) {
    context.setVolumeState(LowVolume());
  }

  @override
  void volumeDown(VolumeContext context) {
    // Already muted, do nothing
  }

  @override
  String getVolumeStatus() => "Muted";
}

class LowVolume implements VolumeState {
  @override
  void volumeUp(VolumeContext context) {
    context.setVolumeState(HighVolume());
  }

  @override
  void volumeDown(VolumeContext context) {
    context.setVolumeState(MutedVolume());
  }

  @override
  String getVolumeStatus() => "Low";
}

class HighVolume implements VolumeState {
  @override
  void volumeUp(VolumeContext context) {
    // Already at max, do nothing
  }

  @override
  void volumeDown(VolumeContext context) {
    context.setVolumeState(LowVolume());
  }

  @override
  String getVolumeStatus() => "High";
}

3. Context (上下文)

class VolumeContext {
  VolumeState _state = MutedVolume();

  void setVolumeState(VolumeState state) {
    _state = state;
  }

  void volumeUp() {
    _state.volumeUp(this);
  }

  void volumeDown() {
    _state.volumeDown(this);
  }

  String getVolumeStatus() {
    return _state.getVolumeStatus();
  }
}

Volume Demo:

class VolumeControlWidget extends StatefulWidget {
  @override
  _VolumeControlWidgetState createState() => _VolumeControlWidgetState();
}

class _VolumeControlWidgetState extends State<VolumeControlWidget> {
  final VolumeContext _volumeContext = VolumeContext();

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text("Volume: ${_volumeContext.getVolumeStatus()}"),
        ElevatedButton(
          onPressed: () {
            _volumeContext.volumeUp();
            setState(() {});
          },
          child: Text("Volume Up"),
        ),
        ElevatedButton(
          onPressed: () {
            _volumeContext.volumeDown();
            setState(() {});
          },
          child: Text("Volume Down"),
        ),
      ],
    );
  }
}
void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Volume Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(title: const Text("Volume Demo")),
        body: VolumeControlWidget(),
      ),
    );
  }
}

VolumeControlWidget 控制音量。通过状态模式清晰地定义了音量的每个状态(静音、低音量、高音量)及其对应的行为。这样,当需要添加新的音量状态或更改音量行为时,代码仍然保持清晰和易于维护。

通过使用状态模式,我们避免了在 VolumeControlWidget 中使用大量的条件语句,从而使代码更加整洁。

场景二:订单处理系统

在一个电商应用中,订单会经历多种状态:已创建、已支付、已发货、已完成、已取消等。每种状态下,订单可以执行的操作和行为都是不同的。例如,只有在“已创建”状态下的订单才可以被支付,而已完成或已取消的订单不能再被修改。

这是一个典型的场景,其中状态模式可以帮助我们清晰、灵活地管理订单的状态和行为。

1. State (状态接口)

abstract class OrderState {
  void pay(OrderContext context);
  void ship(OrderContext context);
  void cancel(OrderContext context);
  String getStatus();
}

2. ConcreteState (具体状态)

class CreatedOrder implements OrderState {
  @override
  void pay(OrderContext context) {
    context.setState(PaidOrder());
  }

  @override
  void ship(OrderContext context) {
    // Cannot ship an order which is not paid
  }

  @override
  void cancel(OrderContext context) {
    context.setState(CancelledOrder());
  }

  @override
  String getStatus() => "Created";
}

class PaidOrder implements OrderState {
  @override
  void pay(OrderContext context) {
    // Already paid, do nothing
  }

  @override
  void ship(OrderContext context) {
    context.setState(ShippedOrder());
  }

  @override
  void cancel(OrderContext context) {
    // Refund logic
    context.setState(CancelledOrder());
  }

  @override
  String getStatus() => "Paid";
}

class ShippedOrder implements OrderState {
  @override
  void pay(OrderContext context) {
    // Already paid, do nothing
  }

  @override
  void ship(OrderContext context) {
    // Already shipped, do nothing
  }

  @override
  void cancel(OrderContext context) {
    // Cannot cancel a shipped order
  }

  @override
  String getStatus() => "Shipped";
}

class CancelledOrder implements OrderState {
  @override
  void pay(OrderContext context) {
    // Cannot pay a cancelled order
  }

  @override
  void ship(OrderContext context) {
    // Cannot ship a cancelled order
  }

  @override
  void cancel(OrderContext context) {
    // Already cancelled, do nothing
  }

  @override
  String getStatus() => "Cancelled";
}

3. Context (上下文)

class OrderContext {
  OrderState _state = CreatedOrder();

  void setState(OrderState state) {
    _state = state;
  }

  void pay() {
    _state.pay(this);
  }

  void ship() {
    _state.ship(this);
  }

  void cancel() {
    _state.cancel(this);
  }

  String getStatus() {
    return _state.getStatus();
  }
}

OrderManagementWidget:

class OrderManagementWidget extends StatefulWidget {
  @override
  _OrderManagementWidgetState createState() => _OrderManagementWidgetState();
}

class _OrderManagementWidgetState extends State<OrderManagementWidget> {
  final OrderContext _orderContext = OrderContext();

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text("Order Status: ${_orderContext.getStatus()}"),
        ElevatedButton(
          onPressed: () {
            _orderContext.pay();
            setState(() {});
          },
          child: Text("Pay"),
        ),
        ElevatedButton(
          onPressed: () {
            _orderContext.ship();
            setState(() {});
          },
          child: Text("Ship"),
        ),
        ElevatedButton(
          onPressed: () {
            _orderContext.cancel();
            setState(() {});
          },
          child: Text("Cancel"),
        ),
      ],
    );
  }
}
void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Pay Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(title: const Text("Pay Demo")),
        body: OrderManagementWidget(),
      ),
    );
  }
}

这种结构确保了每个订单状态的逻辑都被清晰地封装在各自的状态类中。例如,退款逻辑可以被放入 PaidOrdercancel 方法中,而不会影响到其他状态。这种方式提供了一个清晰、模块化的方法来处理复杂的订单状态和相关的业务逻辑。

总结

状态模式是一种设计模式,用于处理对象在其生命周期中可能会经历的不同状态,以及在每种状态下的行为。这种模式的核心思想是封装变化,确保每种状态的行为在其对应的状态类中被清晰定义,从而使得代码更加模块化和可维护。

音量控制

音乐播放应用中,可能需要处理不同的音量状态,如静音、低音量和高音量。使用状态模式,可以为每种音量状态定义一个具体的状态类,并在这些类中定义该状态下的行为。可以清晰地管理音量的状态和行为,避免了复杂的条件语句,使代码更加整洁。

订单处理系统

在电商应用中,订单是核心的业务实体,它可能会经历多种状态,如已创建、已支付、已发货、已完成和已取消。每种状态下的订单都有其特定的行为。例如,只有在“已创建”状态下的订单才可以被支付。使用状态模式,可以为每种订单状态定义一个具体的状态类,并在这些类中定义该状态下的行为。可以清晰地管理订单的状态和行为,确保每次状态转换都是有效和安全的。

结论

状态模式为Flutter应用提供了一个强大而灵活的工具,用于处理复杂的业务状态管理问题。通过使用状态模式,可以确保每种状态的行为都在其对应的状态类中被清晰定义,从而使代码更加模块化和可维护。此外,状态模式还可以帮助我们避免复杂的条件语句,使代码更加整洁。

无论是简单的音量控制,还是复杂的订单处理系统,状态模式都为我们提供了一个优雅的解决方案,使我们能够更加专注于业务逻辑,而不是复杂的状态管理问题。

希望对您有所帮助谢谢!!!

全部评论

相关推荐

点赞 收藏 评论
分享
牛客网
牛客企业服务