一、定义
状态模式是一种行为型设计模式,它允许对象在内部状态改变时改变其行为,使得对象看起来好像修改了它的类。
核心思想:将对象的状态封装为独立的状态类,对象在不同状态下的行为由对应状态类实现,而非通过大量 if-else 或 switch 判断。
二、优缺点
优点:
-
封装了状态的转换逻辑和不同状态下的行为,符合单一职责原则。
-
避免了冗长的条件判断语句,代码可读性和可维护性更高。
-
新增状态时只需扩展状态类,符合开闭原则。
-
状态转换的逻辑清晰,便于跟踪和调试。
缺点:
- 状态较多时,会导致状态类数量激增,增加系统复杂度。
- 状态类之间可能存在依赖关系,增加了维护难度。
三、应用场景
- 对象的行为依赖于其状态,且状态会频繁变化(如订单状态、支付状态)。
- 代码中存在大量与状态相关的条件判断(if-else/switch),且难以维护。
- 需要动态切换对象的行为,且不同行为之间有明确的状态边界(如游戏角色的 "存活""受伤""死亡" 状态)。
四、关键点总结
角色划分:
- 环境类(Context):维护当前状态,负责状态切换,对外提供交互接口。
- 抽象状态类(State):定义所有状态的公共行为接口。
- 具体状态类(ConcreteState):实现抽象状态类,定义特定状态下的行为,并可触发状态转换。
核心逻辑
环境类通过委托当前状态对象处理请求,状态转换由具体状态类内部或环境类控制。
状态独立性
每个状态类专注于自身行为,降低耦合。
五、C# 代码示例
以 "订单状态流转" 为例,订单有 "待支付""已支付""已发货""已完成" 四种状态,每种状态下的行为(如支付、发货、确认收货)不同。
csharp
using System;
// 抽象状态类:定义订单状态的行为接口
public abstract class OrderState
{
protected OrderContext context;
// 设置环境类引用(用于状态切换)
public void SetContext(OrderContext context)
{
this.context = context;
}
// 抽象方法:支付订单
public abstract void Pay();
// 抽象方法:发货
public abstract void Ship();
// 抽象方法:确认收货
public abstract void ConfirmReceipt();
}
// 具体状态:待支付
public class PendingPaymentState : OrderState
{
public override void Pay()
{
Console.WriteLine("订单已支付,状态更新为【已支付】");
// 切换状态为"已支付"
context.State = new PaidState();
context.State.SetContext(context);
}
public override void Ship()
{
Console.WriteLine("错误:订单未支付,无法发货");
}
public override void ConfirmReceipt()
{
Console.WriteLine("错误:订单未支付,无法确认收货");
}
}
// 具体状态:已支付
public class PaidState : OrderState
{
public override void Pay()
{
Console.WriteLine("错误:订单已支付,无需重复支付");
}
public override void Ship()
{
Console.WriteLine("订单已发货,状态更新为【已发货】");
// 切换状态为"已发货"
context.State = new ShippedState();
context.State.SetContext(context);
}
public override void ConfirmReceipt()
{
Console.WriteLine("错误:订单未发货,无法确认收货");
}
}
// 具体状态:已发货
public class ShippedState : OrderState
{
public override void Pay()
{
Console.WriteLine("错误:订单已支付,无需重复支付");
}
public override void Ship()
{
Console.WriteLine("错误:订单已发货,无需重复发货");
}
public override void ConfirmReceipt()
{
Console.WriteLine("订单已确认收货,状态更新为【已完成】");
// 切换状态为"已完成"
context.State = new CompletedState();
context.State.SetContext(context);
}
}
// 具体状态:已完成
public class CompletedState : OrderState
{
public override void Pay()
{
Console.WriteLine("错误:订单已完成,无需支付");
}
public override void Ship()
{
Console.WriteLine("错误:订单已完成,无需发货");
}
public override void ConfirmReceipt()
{
Console.WriteLine("错误:订单已完成,无需重复确认");
}
}
// 环境类:订单上下文,维护当前状态并提供交互接口
public class OrderContext
{
private OrderState _state;
// 初始状态为"待支付"
public OrderContext()
{
_state = new PendingPaymentState();
_state.SetContext(this);
}
// 当前状态(用于状态切换)
public OrderState State
{
get => _state;
set => _state = value;
}
// 委托当前状态处理支付
public void PayOrder()
{
_state.Pay();
}
// 委托当前状态处理发货
public void ShipOrder()
{
_state.Ship();
}
// 委托当前状态处理确认收货
public void ConfirmOrder()
{
_state.ConfirmReceipt();
}
}
// 客户端调用
class Client
{
static void Main(string[] args)
{
OrderContext order = new OrderContext();
// 模拟订单流程
order.PayOrder(); // 订单已支付,状态更新为【已支付】
order.ShipOrder(); // 订单已发货,状态更新为【已发货】
order.ConfirmOrder();// 订单已确认收货,状态更新为【已完成】
order.PayOrder(); // 错误:订单已完成,无需支付
}
}
输出结果:
csharp
订单已支付,状态更新为【已支付】
订单已发货,状态更新为【已发货】
订单已确认收货,状态更新为【已完成】
错误:订单已完成,无需支付
说明:
- 环境类 OrderContext 封装了订单状态的切换逻辑,客户端只需调用其公共方法(如 PayOrder)。
- 每个具体状态类(如 PendingPaymentState)实现了对应状态下的行为,并在合适时机触发状态转换。
- 新增状态(如 "已取消")时,只需添加新的具体状态类,无需修改现有代码,符合开闭原则。
