🌈 个人主页:danci_
🔥 系列专栏:《设计模式》
💪🏻 制定明确可量化的目标,并且坚持默默的做事。
探索设计模式的魅力:状态模式揭秘-如何优雅地处理复杂状态转换
一、案例场景🔍
1.1 经典的运用场景
状态模式是一种行为设计模式,它允许对象在其内部状态改变时改变它的行为。这个模式使得对象看起来好像修改了它的类。以下是几个状态模式的经典场景:
- ✨订单处理系统: 在电商系统中,订单的状态可能会经历多个阶段,如“待支付”、“已支付”、“待发货”、“已发货”、“已完成”等。每个状态对应不同的行为,比如“待支付”状态下可以进行支付操作,“已发货”状态下可以查询物流信息等。通过状态模式,我们可以清晰地管理订单的状态转换和相关行为。
- ✨交通信号灯控制系统: 交通信号灯有红、黄、绿三种状态,每种状态下信号灯的行为是不同的。比如红灯亮时,车辆需要停止;绿灯亮时,车辆可以通行;黄灯亮时,车辆需要准备停止。通过状态模式,我们可以方便地实现信号灯的状态转换和控制逻辑。
- ✨用户登录状态管理: 在用户登录系统中,用户的状态可以分为“未登录”、“已登录”等。不同状态下,用户的权限和操作是不同的。比如未登录状态下,用户只能浏览部分页面;已登录状态下,用户可以访问更多功能和页面。通过状态模式,我们可以实现用户登录状态的管理和权限控制。
下面我们来实现✨订单处理系统。
1.2 一坨坨代码实现😻
使用一个简单的状态机和状态枚举来实现这个场景。下面是一个基于Java的简单实现示例:
- 首先,定义一个表示订单状态的枚举:
public enum OrderStatus {
PENDING_PAYMENT("待支付"),
PAYMENT_RECEIVED("已支付"),
SHIPPED("已发货"),
COMPLETED("已完成");
private final String displayName;
OrderStatus(String displayName) {
this.displayName = displayName;
}
public String getDisplayName() {
return displayName;
}
// 可以根据需要添加更多方法,比如判断是否可以转换到某个状态等
}
- 接下来,我们定义一个Order类来表示订单及其状态转换的逻辑:
public class Order {
private OrderStatus status;
private String orderId;
public Order(String orderId) {
this.orderId = orderId;
this.status = OrderStatus.PENDING_PAYMENT; // 初始状态为待支付
}
public String getOrderId() {
return orderId;
}
public OrderStatus getStatus() {
return status;
}
// 状态转换方法
public void pay() {
if (status == OrderStatus.PENDING_PAYMENT) {
status = OrderStatus.PAYMENT_RECEIVED;
System.out.println("Order " + orderId + " status changed to: " + status.getDisplayName());
} else {
System.out.println("Cannot pay for order " + orderId + " in its current state.");
}
}
public void ship() {
if (status == OrderStatus.PAYMENT_RECEIVED) {
status = OrderStatus.SHIPPED;
System.out.println("Order " + orderId + " status changed to: " + status.getDisplayName());
} else {
System.out.println("Cannot ship order " + orderId + " in its current state.");
}
}
public void complete() {
if (status == OrderStatus.SHIPPED) {
status = OrderStatus.COMPLETED;
System.out.println("Order " + orderId + " status changed to: " + status.getDisplayName());
} else {
System.out.println("Cannot complete order " + orderId + " in its current state.");
}
}
// 可以根据需要添加更多方法和逻辑
}
- 现在,你可以通过创建一个Order对象并调用其状态转换方法来模拟订单的状态变化:
public class OrderProcessingDemo {
public static void main(String[] args) {
Order order = new Order("12345");
System.out.println("Current order status: " + order.getStatus().getDisplayName());
order.pay();
System.out.println("Current order status: " + order.getStatus().getDisplayName());
order.ship();
System.out.println("Current order status: " + order.getStatus().getDisplayName());
order.complete();
System.out.println("Current order status: " + order.getStatus().getDisplayName());
// 尝试在非法状态下进行状态转换
order.pay(); // 应该不会成功,因为订单已经完成
}
}
在这个实现中,我们直接在Order类中处理了状态转换的逻辑。每个状态转换方法(pay、ship、complete)都会检查当前状态是否允许进行转换,并相应地更新状态或打印错误消息。虽然这个实现很简单,但它缺乏一些设计模式所提供的灵活性和可扩展性。在实际项目中,你可能会考虑使用状态模式、策略模式或模板方法模式等来实现更复杂的状态管理逻辑。然而,根据题目的要求,这里提供了一个不使用设计模式的简单实现。
1.3 痛点
上述实现确实存在一些缺点,下面逐一分析:
-
硬编码的状态转换逻辑:
在上述实现中,状态转换的逻辑是直接硬编码在Order类中的pay、ship和complete方法里。这意味着如果未来需要添加新的状态或更改现有的状态转换逻辑,我们必须修改Order类的源代码。这违反了开闭原则(Open-Closed Principle),即软件实体应该对扩展开放,对修改关闭。 -
缺乏灵活性:
由于状态转换逻辑是固定的,系统无法轻松适应新的业务规则或变化。例如,如果引入了一个新的状态“部分发货”,或者某些状态下允许退款操作,现有的代码结构将难以应对这些变化。 -
状态和行为紧密耦合:
在上述实现中,状态和与状态相关的行为(即状态转换)是紧密耦合的。这导致了高内聚低耦合的设计原则的违反。理想情况下,状态应该只表示数据,而行为应该由独立的组件(如状态机)来管理。 -
可维护性问题:
随着业务逻辑的增长和复杂性的增加,直接在Order类中管理状态转换将变得越来越困难。代码将变得难以理解和维护,特别是当多个开发人员参与项目时。 -
错误处理不足:
在当前实现中,错误处理仅限于打印错误消息到控制台。在实际的生产环境中,通常需要更复杂的错误处理机制,如回滚操作、日志记录、通知用户或管理员等。 -
可扩展性问题:
由于状态转换逻辑直接嵌入在Order类中,因此添加新的状态或转换逻辑需要修改现有的类结构。这可能会引入新的错误并破坏现有功能的稳定性。此外,每次添加新功能时都需要重新测试和验证整个系统。
二、解决方案
使用状态模式可以有效地解决上述提到的一些缺点。状态模式允许一个对象在其内部状态改变时改变它的行为,使得对象看起来好像修改了它的类。在状态模式中,我们定义状态和状态之间的转换,将状态转换的逻辑封装在状态对象自身中,而不是将其分散在多个条件语句中。
2.1 定义
状态模式(State Pattern)是一种行为设计模式,它允许一个对象在其内部状态改变时改变它的行为。状态模式把与特定状态相关的行为封装到一个个的类中,当对象的状态改变时,它的行为也会随着改变。 |
2.2 案例分析🧐
2.3 状态模式结构图及说明
主要组件:
- Context(上下文):它定义了客户感兴趣的接口,并且维护一个具体状态对象的实例,这个具体状态对象定义了当前状态。
- State(抽象状态):这是一个抽象类,它定义了状态转换的接口,这个接口由具体状态类实现。
- ConcreteState(具体状态):具体状态类实现了抽象状态定义的接口,从而实现状态转换和具体行为。
说明:
- 封装了状态的转换逻辑:状态模式将状态转换逻辑封装在状态类中,而不是散布在多个条件语句中。这有助于减少代码的复杂性,并使状态转换逻辑更易于理解和维护。
- 增加新的状态或行为变得更容易:由于状态和行为都被封装在单独的类中,因此添加新的状态或行为只需要添加新的状态类,而不需要修改其他代码。这符合开闭原则,即对扩展开放,对修改封闭。
- 状态转换的显式化:状态模式使得状态转换变得显式化,因为状态的转换是通过调用状态对象的方法来实现的,而不是通过修改上下文的状态变量来实现的。这使得状态转换更加清晰和可预测。
- 过多的状态类可能导致类爆炸:如果一个对象有很多状态,并且每个状态的行为差异很大,那么可能需要为每个状态创建一个单独的类。这可能会导致类数量过多,增加系统的复杂性。
2.4 使用状态模式重构示例
使用状态模式来实现上述场景时,首先需要定义状态接口和具体的状态类,然后在上下文类(如Order)中维护一个对当前状态的引用。状态类将封装与特定状态相关的行为,包括状态转换。
下面是一个使用状态模式实现的订单处理系统的示例:
- 状态接口
public interface OrderState {
void pay(Order order);
void ship(Order order);
void complete(Order order);
// 可能还需要其他方法,如退款、部分发货等
}
- 具体的状态类
public class CreatedState implements OrderState {
@Override
public void pay(Order order) {
// 处理支付逻辑
System.out.println("Order paid.");
order.setState(new PaidState());
}
@Override
public void ship(Order order) {
System.out.println("Cannot ship order in Created state.");
}
@Override
public void complete(Order order) {
System.out.println("Cannot complete order in Created state.");
}
}
public class PaidState implements OrderState {
@Override
public void pay(Order order) {
System.out.println("Order already paid.");
}
@Override
public void ship(Order order) {
// 处理发货逻辑
System.out.println("Order shipped.");
order.setState(new ShippedState());
}
@Override
public void complete(Order order) {
System.out.println("Cannot complete order before it is shipped.");
}
}
public class ShippedState implements OrderState {
@Override
public void pay(Order order) {
System.out.println("Order already paid and shipped.");
}
@Override
public void ship(Order order) {
System.out.println("Order already shipped.");
}
@Override
public void complete(Order order) {
// 处理完成订单逻辑
System.out.println("Order completed.");
order.setState(new CompletedState());
}
}
public class CompletedState implements OrderState {
@Override
public void pay(Order order) {
System.out.println("Cannot pay for a completed order.");
}
@Override
public void ship(Order order) {
System.out.println("Cannot ship a completed order.");
}
@Override
public void complete(Order order) {
System.out.println("Order already completed.");
}
}
- 上下文类
public class Order {
private OrderState state;
public Order() {
this.state = new CreatedState(); // 初始状态
}
public void setState(OrderState state) {
this.state = state;
}
public void pay() {
state.pay(this);
}
public void ship() {
state.ship(this);
}
public void complete() {
state.complete(this);
}
// 可能还有其他与订单相关的方法和属性
}
- 使用示例
public class StatePatternDemo {
public static void main(String[] args) {
Order order = new Order();
order.pay(); // 当前状态为CreatedState,调用pay会转移到PaidState
order.ship(); // 当前状态为PaidState,调用ship会转移到ShippedState
order.complete(); // 当前状态为ShippedState,调用complete会转移到CompletedState
order.pay(); // 当前状态为CompletedState,不能再支付
order.ship(); // 当前状态为CompletedState,不能再发货
order.complete(); // 当前状态为CompletedState,订单已完成
}
}
在这个示例中,Order类代表上下文,它有一个state字段来保存当前状态,并且提供了pay、ship和complete等方法来触发状态转换。每个具体的状态类(如CreatedState、PaidState等)都实现了OrderState接口,并定义了在当前状态下这些方法的行为。
注:这只是一个简单的示例,实际应用中可能还需要处理更多的状态和行为,并且状态转换的逻辑可能会更加复杂。此外,为了提高代码的可维护性和可读性,还可以考虑使用枚举类型来定义状态,或者使用状态机框架来管理状态转换。
2.5 重构后解决的问题
使用状态模式可以有效地解决 1.3 痛点 中提到的一些缺点。状态模式允许一个对象在其内部状态改变时改变它的行为,使得对象看起来好像修改了它的类。在状态模式中,我们定义状态和状态之间的转换,将状态转换的逻辑封装在状态对象自身中,而不是将其分散在多个条件语句中。
以下是状态模式如何解决上述缺点的原因:
-
解耦状态和行为:
在状态模式中,状态和行为被封装在单独的状态类中。这意味着Order类不再需要直接处理状态转换的逻辑。每个状态类负责定义在当前状态下允许的行为以及状态转换的规则。这大大减少了Order类的复杂性,并且使得状态和行为之间的关系更加清晰和易于管理。 -
提高灵活性和可扩展性:
由于状态转换逻辑被封装在状态类中,添加新的状态或更改现有状态的行为变得相对容易。我们只需要定义新的状态类并实现相应的行为即可,而不需要修改使用状态模式的上下文类(在本例中是Order类)。这符合开闭原则,即软件实体应该对扩展开放,对修改关闭。 -
更好的错误处理:
在状态模式中,状态类可以定义自己的错误处理逻辑。例如,如果尝试在不合适的状态下执行某个操作,状态类可以抛出异常或返回错误码,而不是在上下文类中打印错误消息。这提供了更好的错误处理机制,并使得错误处理更加集中和一致。 -
提高可维护性:
将状态和行为封装在单独的状态类中使得代码更加模块化和可维护。每个状态类只关注自己的行为和转换规则,这使得代码更加清晰、易于理解和测试。此外,由于状态类之间是松耦合的,因此可以独立地修改和测试它们,而不会影响到其他状态类或使用它们的上下文类。 -
支持复杂的业务逻辑:
状态模式能够轻松地支持复杂的业务逻辑和状态转换规则。通过定义更多的状态类和转换逻辑,我们可以实现更加精细和灵活的状态管理。此外,状态模式还可以与其他设计模式(如策略模式、观察者模式等)结合使用,以构建更加复杂和强大的软件系统。
通过使用状态模式可以解决硬编码的状态转换逻辑、缺乏灵活性、状态和行为紧密耦合、可维护性问题以及错误处理不足等缺点。通过将状态和行为封装在单独的状态类中,我们实现了状态与行为的解耦,提高了系统的灵活性、可扩展性和可维护性。文章来源:https://www.toymoban.com/news/detail-839987.html
转载文章只截取部分内容
详请跳转原文:https://blog.csdn.net/danci_/article/details/136367657文章来源地址https://www.toymoban.com/news/detail-839987.html
到了这里,关于状态模式揭秘-如何优雅地处理复杂状态转换的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!