如果你在处理处于多种状态的对象时,曾感觉被 if-else
语句或 switch
语句搞得晕头转向,那你并不孤单。这些条件判断会让代码变得一团糟——尤其是在管理对象历经不同阶段时的不同行为时更是如此。这时就该状态模式(State Pattern)登场了:它是以一种结构化、易于维护的方式来清晰管理基于状态的行为的方法。
今天,我们将以一个简单的订单处理系统为例,逐步讲解这个概念。我们会探讨如何在不堆砌 if-else
检查的情况下处理状态转换,以及状态模式如何帮助我们保持代码的整洁和可扩展性。
传统状态处理方式的问题:大量的条件判断语句
想象一个简单的订单处理系统,每个订单会经历以下几个阶段:
待处理(Pending)
已支付(Paid)
已发货(Shipped)
已送达(Delivered)
已取消(Cancelled)
在管理每个状态时,我们可能希望:
只对已支付的订单发货。
只对已发货的订单进行派送。
如果订单已送达或已取消,则阻止某些操作。
大多数人通过添加大量的 if-else
检查来处理这个问题。以下是在传统设置中它可能呈现的样子:
public class Order
{
public string Status { get; set; } = "Pending";
public void Pay()
{
if (Status == "Pending")
{
Status = "Paid";
Console.WriteLine("Order has been paid.");
}
else
{
Console.WriteLine("Cannot pay for order in current state: " + Status);
}
}
public void Ship()
{
if (Status == "Paid")
{
Status = "Shipped";
Console.WriteLine("Order has been shipped.");
}
else
{
Console.WriteLine("Cannot ship order in current state: " + Status);
}
}
public void Deliver()
{
if (Status == "Shipped")
{
Status = "Delivered";
Console.WriteLine("Order has been delivered.");
}
else
{
Console.WriteLine("Cannot deliver order in current state: " + Status);
}
}
public void Cancel()
{
if (Status == "Pending" || Status == "Paid")
{
Status = "Cancelled";
Console.WriteLine("Order has been cancelled.");
}
else
{
Console.WriteLine("Cannot cancel order in current state: " + Status);
}
}
}
这种方法有什么问题呢?
条件过多:每个操作都有多个检查条件。随着状态数量的增加,这些检查条件会变得更长,也更难维护。
逻辑分散:每个操作都必须了解所有可能的状态。这会把不同的逻辑混合在一处,使代码变得杂乱无章。
修改困难:如果我们添加更多的状态或操作,就必须更新代码中所有的
if-else
代码块。
使用状态模式:管理状态的更好方法
状态模式允许对象根据其状态改变自身行为,方法是将每个状态的行为组织到各自的类中。通过这种方法:
每个状态都有一个专门的类来处理其相关操作。
我们可以在状态之间进行转换,而无需到处检查条件。
我们能够保持代码整洁、易读且易于扩展。
实现状态模式
让我们逐步进行分解。
步骤 1:创建一个状态接口 我们将定义一个接口 IOrderState
,其中包含订单类(Order
类)所需的每个操作的方法(例如 Pay
、Ship
、Deliver
、Cancel
)。
public interface IOrderState
{
void Pay(Order order);
void Ship(Order order);
void Deliver(Order order);
void Cancel(Order order);
}
步骤 2:为每个状态创建类 现在,每个状态(如待处理、已支付、已发货等)都有自己的类,这些类实现 IOrderState
接口。每个类只处理在该状态下允许执行的操作。
例如,以下是待处理状态(PendingState
)类可能的样子:
public class PendingState : IOrderState
{
public void Pay(Order order)
{
order.State = new PaidState();
Console.WriteLine("Order has been paid.");
}
public void Ship(Order order) => Console.WriteLine("Cannot ship a pending order.");
public void Deliver(Order order) => Console.WriteLine("Cannot deliver a pending order.");
public void Cancel(Order order)
{
order.State = new CancelledState();
Console.WriteLine("Order has been cancelled.");
}
}
以下是已支付状态(PaidState
)类可能的样子:
public class PaidState : IOrderState
{
public void Pay(Order order) => Console.WriteLine("Order is already paid.");
public void Ship(Order order)
{
order.State = new ShippedState();
Console.WriteLine("Order has been shipped.");
}
public void Deliver(Order order) => Console.WriteLine("Cannot deliver a paid order.");
public void Cancel(Order order)
{
order.State = new CancelledState();
Console.WriteLine("Order has been cancelled.");
}
}
每个状态只处理对其有意义的操作,这使得每个类都很小且易于理解。
步骤 3:将订单类定义为上下文 订单类(我们的上下文)维护当前状态。它不再进行 if-else
检查,而是将工作委托给当前状态对应的类来处理。
public class Order
{
public IOrderState State { get; set; } = new PendingState();
public void Pay() => State.Pay(this);
public void Ship() => State.Ship(this);
public void Deliver() => State.Deliver(this);
public void Cancel() => State.Cancel(this);
}
现在,订单类只需将请求传递给它的状态对象,然后状态对象负责处理其余的事情。
测试基于状态的逻辑
让我们看看测试时它是什么样子的。
var order = new Order();
order.Pay(); // 输出:Order has been paid.
order.Ship(); // 输出:Order has been shipped.
order.Deliver(); // 输出:Order has been delivered.
order.Cancel(); // 输出:Cannot cancel a delivered order.
没有 if-else
语句,也没有难以理解的条件判断——只有流畅的、基于状态的转换。
状态模式的优势
代码条理清晰:每个状态类只处理特定状态的逻辑,所以代码更加整洁、有条理。
可扩展性强:如果添加更多状态,只需添加新的类,而无需更改现有逻辑。
可读性好:每个类都清晰地表明了在特定状态下可以做什么和不可以做什么,这使得代码更易于理解和调试。
何时使用基于状态的逻辑
在以下情况下,状态模式很有用:
一个对象有多个状态,且每个状态都有不同的行为。
根据状态执行不同操作时存在复杂的条件判断。
可扩展性很重要——如果你计划添加更多状态或状态转换,这种方法将为你节省时间并减少烦恼。
使用状态模式可以使代码更易于理解、维护和扩展。我们不再处理混乱繁杂的条件判断,而是创建了一个每个状态都有明确职责的系统。这种结构有助于确保随着应用程序的发展,我们的代码依然保持整洁且易于修改。
如果你喜欢我的文章,请给我一个赞!谢谢