欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 会展 > 设计模式之状态模式:自动售货机的喜怒哀乐

设计模式之状态模式:自动售货机的喜怒哀乐

2025/1/1 15:02:01 来源:https://blog.csdn.net/shunda_zhou/article/details/144789312  浏览:    关键词:设计模式之状态模式:自动售货机的喜怒哀乐

在这里插入图片描述

~犬📰余~

“我欲贱而贵,愚而智,贫而富,可乎?
曰:其唯学乎”

一、状态模式概述

\quad 在我们的日常生活中,很多事物都具有不同的状态。比如我们经常使用的自动售货机,它就具有多种状态:空闲状态(等待投币)、已投币状态(等待选择商品)、出货状态(正在出货)等。在每种状态下,售货机对用户的操作会产生不同的响应。例如,在空闲状态下投币,机器会切换到已投币状态;而在已投币状态下投币,机器则会直接退币。
\quad 这种根据不同状态对相同操作做出不同响应的场景在软件开发中非常常见。最直观的解决方案是使用大量的 if-else 或 switch-case 语句来判断当前状态并执行相应的操作。但这样的代码往往会变得臃肿、难以维护,而且违反了"开闭原则"——每次添加新状态都需要修改判断语句。
\quad 状态模式就是为了解决这类问题而设计的。它的核心思想是将不同状态的行为抽象成独立的类,使得状态的切换只需要改变对象的状态属性即可。这样一来,每个状态的行为都被封装在对应的状态类中,各个状态之间互不干扰,系统便具有了良好的可扩展性和维护性。
\quad 让我们看一下状态模式的基本结构:
图片

二、状态模式的角色组成

\quad 状态模式主要由三种角色组成,它们各司其职又相互配合,共同实现了状态转换的灵活管理。就像一台自动售货机需要有机器本身(管理者)、不同的运行状态(状态抽象)以及每种具体状态下的操作逻辑(具体状态实现)一样,状态模式中的每个角色也都有其特定的职责。

  • Context(环境类):相当于状态模式中的"管理者",它维护一个对当前状态对象的引用,并负责状态的切换。在我们的自动售货机例子中,Context 就是售货机本身,它需要知道当前处于什么状态,并根据用户的操作来改变状态。
  • State(抽象状态类):定义了一个接口或抽象类,用于封装与 Context 的一个特定状态相关的行为。就像我们在定义售货机状态时,需要规定所有状态都应该能够处理投币、退币、选择商品等基本操作一样。
  • ConcreteState(具体状态类):实现了抽象状态类定义的接口,为 Context 的每一个具体状态提供实际的行为实现。例如,在售货机的"空闲状态"下,投币操作会将状态切换为"已投币状态";而在"已投币状态"下,投币操作则会直接退回硬币。

\quad 当用户触发某个操作时,Context 会将请求委托给当前的 State 对象来处理。每个 ConcreteState 都知道在当前状态下应该如何处理这个请求,以及在什么情况下需要切换到其他状态。通过这种方式,系统的状态转换逻辑被分散到各个状态类中,避免了在一个类中堆积大量的条件判断语句。

三、状态模式案例

\quad 为了更好地理解状态模式的实际应用,让我们以自动售货机为例来实现一个完整的状态模式示例。首先看一下自动售货机的状态转换图:
在这里插入图片描述

\quad 现在让我们通过代码来实现这个自动售货机系统:

// 抽象状态类
public abstract class VendingMachineState {protected VendingMachine machine;public VendingMachineState(VendingMachine machine) {this.machine = machine;}// 投币操作public abstract void insertCoin();// 退币操作public abstract void ejectCoin();// 选择商品public abstract void selectProduct();// 发放商品public abstract void dispense();
}// 空闲状态(等待投币)
public class IdleState extends VendingMachineState {public IdleState(VendingMachine machine) {super(machine);}@Overridepublic void insertCoin() {System.out.println("投币成功");machine.setState(new HasCoinState(machine));}@Overridepublic void ejectCoin() {System.out.println("没有硬币,无法退币");}@Overridepublic void selectProduct() {System.out.println("请先投币");}@Overridepublic void dispense() {System.out.println("请先投币");}
}// 已投币状态
public class HasCoinState extends VendingMachineState {public HasCoinState(VendingMachine machine) {super(machine);}@Overridepublic void insertCoin() {System.out.println("已经有硬币,无需再投");}@Overridepublic void ejectCoin() {System.out.println("退币成功");machine.setState(new IdleState(machine));}@Overridepublic void selectProduct() {System.out.println("商品选择成功,正在出货...");machine.setState(new DispensingState(machine));}@Overridepublic void dispense() {System.out.println("请先选择商品");}
}// 出货状态
public class DispensingState extends VendingMachineState {public DispensingState(VendingMachine machine) {super(machine);}@Overridepublic void insertCoin() {System.out.println("正在出货,请稍等");}@Overridepublic void ejectCoin() {System.out.println("正在出货,无法退币");}@Overridepublic void selectProduct() {System.out.println("正在出货,请稍等");}@Overridepublic void dispense() {System.out.println("商品已发放");machine.setState(new IdleState(machine));}
}// 自动售货机类(Context)
public class VendingMachine {private VendingMachineState state;public VendingMachine() {state = new IdleState(this);}public void setState(VendingMachineState state) {this.state = state;}public void insertCoin() {state.insertCoin();}public void ejectCoin() {state.ejectCoin();}public void selectProduct() {state.selectProduct();state.dispense();}
}// 测试类
public class StatePatternDemo {public static void main(String[] args) {VendingMachine machine = new VendingMachine();// 测试初始状态(空闲状态)System.out.println("===== 测试空闲状态 =====");machine.selectProduct();  // 请先投币machine.ejectCoin();      // 没有硬币,无法退币// 测试投币System.out.println("\n===== 测试投币 =====");machine.insertCoin();     // 投币成功machine.insertCoin();     // 已经有硬币,无需再投// 测试选择商品System.out.println("\n===== 测试选择商品 =====");machine.selectProduct();  // 商品选择成功,正在出货... -> 商品已发放// 测试回到初始状态System.out.println("\n===== 测试回到初始状态 =====");machine.selectProduct();  // 请先投币}
}

\quad 测试结果:
图片
\quad 在这个自动售货机的实现中,我们可以看到状态模式的几个关键特点:

  • 状态的封装:每个状态都被封装在独立的类中,包含了该状态下所有可能的行为实现。例如,IdleState 类处理了空闲状态下的所有操作响应。
  • 状态转换的解耦:状态之间的转换被封装在各个状态类内部。比如,当在空闲状态下投币时,IdleState 类负责创建新的 HasCoinState 对象并更新机器状态。
  • Context 类的简化:VendingMachine 类不需要关心具体的状态处理逻辑,它只需要将请求委托给当前状态对象即可。这使得 Context 类变得非常简洁。
  • 行为的一致性:通过抽象状态类 VendingMachineState 的定义,确保了所有具体状态类都实现了统一的接口,便于系统的扩展和维护。

\quad 运行这段代码,我们可以看到自动售货机在不同状态下对相同操作会产生不同的响应,这正是状态模式的精髓所在。

四、状态模式的优缺点

4.1. 优点

\quad 状态模式最大的优势在于它将状态相关的行为分散到独立的类中。比如在我们的自动售货机示例中,每个状态(空闲、已投币、出货)的处理逻辑都被清晰地封装在对应的类中。这种设计带来了良好的代码组织结构:当我们需要修改某个状态的行为时,只需要修改对应的状态类,不会影响到其他状态的代码。
\quad 其次,状态模式消除了传统实现中大量的条件语句。如果不使用状态模式,我们可能需要在售货机类中编写大量的 if-else 来判断当前状态并执行相应的操作。而使用状态模式后,这些判断都被转化为了多态调用,代码更加优雅和易于维护。
\quad 状态模式还使得状态转换变得更加明确和可控。每个状态类都清楚地知道在什么情况下需要切换到其他状态,这种显式的状态管理方式大大降低了出错的可能性。

4.2. 缺点

\quad 最明显的缺点是可能会导致类的数量增多。在我们的示例中仅有三个状态,就需要创建三个状态类。如果系统的状态数量很多,就会产生大量的状态类,这会增加系统的复杂度。
\quad 另外,状态模式将状态转换的逻辑分散在各个状态类中,虽然这提高了代码的清晰度,但也使得状态之间的转换关系变得不那么直观。我们需要查看多个状态类才能理清楚完整的状态转换图。

五、状态模式的适用场景

\quad 状态模式在实际开发中有着广泛的应用场景。除了我们讨论的自动售货机示例,它还特别适用于以下场景:

  • 游戏开发中的角色状态管理就是一个典型的应用场景。比如游戏角色可能有站立、行走、跳跃、攻击等多个状态,在每个状态下对用户输入的响应都不同。使用状态模式可以将这些复杂的状态行为清晰地组织起来。
  • 订单系统也经常使用状态模式来管理订单状态。一个订单可能经历待支付、已支付、已发货、已签收等多个状态,每个状态下允许的操作都不同。状态模式可以帮助我们构建一个清晰的订单状态管理框架。
  • 文档处理系统中的文档状态(草稿、审核中、已发布等)、TCP 连接状态管理(连接中、已连接、断开连接等)都是状态模式的适用场景。总的来说,当一个对象需要在多个状态之间切换,并且在不同状态下对外呈现不同的行为时,状态模式都是一个很好的选择。

\quad 在选择是否使用状态模式时,我们需要权衡系统的状态数量和状态转换的复杂度。如果系统只有两三个状态,且状态转换逻辑相对简单,使用普通的条件语句可能更加直观。但如果系统包含多个状态,且状态之间的转换规则复杂,那么状态模式就能够很好地发挥其优势。

六、总结

\quad 状态模式为我们提供了一种优雅的方式来管理对象的状态变化。通过将不同状态的行为封装到独立的类中,它实现了更好的代码组织结构和更清晰的状态转换逻辑。就像我们在自动售货机示例中看到的,状态模式不仅使代码更容易理解和维护,还提供了良好的扩展性。
\quad 在实际应用中,状态模式的价值不仅体现在消除了复杂的条件判断,更重要的是它遵循了"开闭原则",使得我们可以在不修改现有代码的情况下添加新的状态。这一点在大型系统的开发和维护中尤为重要。
\quad 值得注意的是,状态模式并不是管理状态的唯一方案。在选择使用状态模式时,我们需要考虑系统的具体需求,权衡代码的复杂度和灵活性。有时候,简单的条件语句可能是更好的选择。设计模式的使用不是目的,解决问题才是关键。
在这里插入图片描述

关注犬余,共同进步

技术从此不孤单

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com