一、状态模式概述
状态模式(State Pattern)是一种行为型设计模式,它允许对象在其内部状态改变时改变其行为,使对象看起来像是修改了它的类。这种模式将特定状态相关的行为局部化,并且将不同状态的行为分割开来。
状态模式的核心价值:
-
消除庞大的条件语句:替代对象行为中基于状态的if-else或switch-case语句
-
状态转换显式化:将状态转换逻辑组织在单一位置
-
符合开闭原则:新增状态无需修改现有状态类
二、状态模式结构
经典UML类图:
classDiagram
class Context {
-State _state
+Request()
+State
}
interface IState {
<<interface>>
+Handle(Context context)
}
class ConcreteStateA {
+Handle(Context context)
}
class ConcreteStateB {
+Handle(Context context)
}
Context o--> IState
IState <|-- ConcreteStateA
IState <|-- ConcreteStateB
结构组成:
-
Context(上下文):维护一个ConcreteState子类的实例
-
State(状态接口):定义所有具体状态的共同接口
-
ConcreteState(具体状态):实现与上下文特定状态相关的行为
三、C#实现示例:订单状态系统
基础实现版本:
// 状态接口
public interface IOrderState
{
void Process(Order order);
void Ship(Order order);
void Cancel(Order order);
}
// 具体状态:新建状态
public class NewOrderState : IOrderState
{
public void Process(Order order)
{
Console.WriteLine("开始处理订单...");
order.SetState(new ProcessingOrderState());
}
public void Ship(Order order) =>
Console.WriteLine("订单尚未处理,不能发货!");
public void Cancel(Order order)
{
Console.WriteLine("取消新订单");
order.SetState(new CancelledOrderState());
}
}
// 具体状态:处理中状态
public class ProcessingOrderState : IOrderState
{
public void Process(Order order) =>
Console.WriteLine("订单已在处理中");
public void Ship(Order order)
{
Console.WriteLine("订单已发货");
order.SetState(new ShippedOrderState());
}
public void Cancel(Order order)
{
Console.WriteLine("取消处理中的订单");
order.SetState(new CancelledOrderState());
}
}
// 上下文类
public class Order
{
private IOrderState _state;
public Order()
{
_state = new NewOrderState();
}
public void SetState(IOrderState state) => _state = state;
public void Process() => _state.Process(this);
public void Ship() => _state.Ship(this);
public void Cancel() => _state.Cancel(this);
}
使用示例:
var order = new Order();
order.Process(); // 开始处理订单...
order.Ship(); // 订单已发货
order.Cancel(); // 订单已发货,无法取消
四、高级实现技巧
1. 状态转换表驱动
// 使用字典管理状态转换规则
public class OrderStateMachine
{
private readonly Dictionary<Type, StateTransitions> _transitions;
public OrderStateMachine()
{
_transitions = new Dictionary<Type, StateTransitions>
{
[typeof(NewOrderState)] = new StateTransitions
{
{ OrderAction.Process, typeof(ProcessingOrderState) },
{ OrderAction.Cancel, typeof(CancelledOrderState) }
},
// 其他状态转换规则...
};
}
public Type GetNextState(Type current, OrderAction action)
=> _transitions[current][action];
}
2. 结合依赖注入
// 在Startup.cs中注册状态
services.AddTransient<NewOrderState>();
services.AddTransient<ProcessingOrderState>();
// 其他状态...
// 修改上下文类使用DI
public class Order
{
private IOrderState _state;
private readonly IServiceProvider _services;
public Order(IServiceProvider services)
{
_services = services;
_state = _services.GetRequiredService<NewOrderState>();
}
public void SetState<T>() where T : IOrderState
=> _state = _services.GetRequiredService<T>();
}
五、状态模式最佳实践
1. 何时使用状态模式:
-
对象的行为取决于它的状态,并且必须在运行时根据状态改变行为
-
操作中包含大量与对象状态相关的条件语句
-
当状态数量超过5个且可能继续增加时
2. 性能优化策略:
-
状态对象复用:无状态的状态对象可以设计为单例
-
缓存状态转换:预计算并缓存可能的转换路径
-
异步状态处理:对耗时操作实现异步状态处理
// 异步状态接口
public interface IAsyncOrderState
{
Task ProcessAsync(Order order);
Task ShipAsync(Order order);
Task CancelAsync(Order order);
}
3. 与其它模式的结合:
-
策略模式:状态模式可以视为策略模式的扩展,但策略模式不处理状态转换
-
观察者模式:在状态变更时通知相关观察者
-
备忘录模式:实现状态历史回溯
六、实际应用案例
电商订单系统状态图:
stateDiagram-v2
[*] --> New
New --> Processing: 处理订单
Processing --> Shipped: 发货
Processing --> Cancelled: 取消
Shipped --> Delivered: 送达
Shipped --> Returned: 退货
Delivered --> Returned: 退货
Cancelled --> [*]
Returned --> [*]
游戏角色状态实现:
public class Player
{
private IPlayerState _state;
public void Attack() => _state.Attack(this);
public void Move() => _state.Move(this);
// 状态切换方法
public void TakeDamage() => SetState(new HurtState());
public void Heal() => SetState(new NormalState());
}
public interface IPlayerState
{
void Attack(Player player);
void Move(Player player);
}
public class NormalState : IPlayerState
{
public void Attack(Player player) => Console.WriteLine("造成100%伤害");
public void Move(Player player) => Console.WriteLine("100%移动速度");
}
public class HurtState : IPlayerState
{
public void Attack(Player player) => Console.WriteLine("造成70%伤害");
public void Move(Player player) => Console.WriteLine("80%移动速度");
}
七、状态模式优缺点分析
优点:
-
单一职责原则:将与特定状态相关的代码放在独立的类中
-
开闭原则:无需修改已有状态类和上下文就能引入新状态
-
消除庞大的条件分支语句
缺点:
-
可能过度设计:如果状态很少或很少改变,会增加不必要的复杂性
-
状态对象间可能产生耦合:状态转换需要了解其他状态
-
性能开销:频繁创建状态对象可能带来开销(可通过对象池优化)
八、总结
状态模式是处理复杂状态逻辑的强大工具,在C#中通过接口和具体类的组合可以优雅地实现。在实际开发中,建议:
-
对状态超过3个且可能增长的系统优先考虑
-
结合DI容器管理状态对象生命周期
-
对复杂状态转换使用状态机模式增强
-
考虑使用状态模式库(如Stateless)处理复杂场景
通过合理应用状态模式,可以使代码更易维护、扩展性更强,特别是在业务规则频繁变化的领域(如订单系统、游戏开发、工作流引擎等)能显著提升代码质量。