2. 自动售货机状态机实现

任务目标

用有限状态机(FSM)建模自动售货机的核心业务状态:空闲、投币、选择商品、出货、找零,并处理余额不足、商品缺货、取消交易等异常情况。实现一个可直接运行的Python控制台程序,通过输入命令驱动状态迁移,并输出每个动作后的状态与结果。

状态与事件定义

状态含义可触发事件
IDLE空闲,等待投币insert_coin(金额), cancel
COIN_INSERTED已投币,等待选择商品select(商品编号), insert_coin(追加金额), cancel
DISPENSE出货中(瞬时状态)自动迁移到找零或空闲
CHANGE找零中(瞬时状态)自动迁移到空闲

异常情况处理:余额不足时拒绝出货并提示;商品缺货时拒绝出货并提示;取消交易时退还全部已投金额。

完整实现代码

class VendingMachineFSM:
    def __init__(self):
        self.state = "IDLE"
        self.balance = 0
        # 商品表:编号 -> (名称, 价格, 库存)
        self.products = {
            "A1": ("可乐", 3, 5),
            "A2": ("雪碧", 3, 0),   # 缺货示例
            "B1": ("薯片", 5, 2),
        }

    def insert_coin(self, amount):
        if amount <= 0:
            print("无效金额")
            return
        if self.state == "IDLE":
            self.balance = amount
            self.state = "COIN_INSERTED"
            print(f"投币{amount}元,当前余额{self.balance}元")
        elif self.state == "COIN_INSERTED":
            self.balance += amount
            print(f"追加投币{amount}元,当前余额{self.balance}元")
        else:
            print("当前状态不允许投币")

    def select(self, code):
        if self.state != "COIN_INSERTED":
            print("请先投币")
            return
        if code not in self.products:
            print("商品编号不存在")
            return
        name, price, stock = self.products[code]
        if stock <= 0:
            print(f"商品{name}缺货,已退还{self.balance}元")
            self.balance = 0
            self.state = "IDLE"
            return
        if self.balance < price:
            print(f"余额不足,{name}需{price}元,当前余额{self.balance}元")
            return
        # 正常出货
        self.products[code] = (name, price, stock - 1)
        self.balance -= price
        print(f"出货:{name},扣除{price}元,剩余余额{self.balance}元")
        if self.balance > 0:
            self.state = "CHANGE"
            print(f"找零{self.balance}元")
            self.balance = 0
        self.state = "IDLE"
        print("回到空闲状态")

    def cancel(self):
        if self.state == "IDLE":
            print("当前无交易可取消")
            return
        if self.balance > 0:
            print(f"取消交易,退还{self.balance}元")
            self.balance = 0
        self.state = "IDLE"
        print("回到空闲状态")

    def run(self):
        print("自动售货机已启动。命令:insert_coin 金额 | select 商品编号 | cancel | exit")
        while True:
            cmd = input("> ").strip().split()
            if not cmd:
                continue
            action = cmd[0].lower()
            if action == "exit":
                break
            elif action == "insert_coin" and len(cmd) > 1:
                self.insert_coin(int(cmd[1]))
            elif action == "select" and len(cmd) > 1:
                self.select(cmd[1].upper())
            elif action == "cancel":
                self.cancel()
            else:
                print("无效命令")

if __name__ == "__main__":
    VendingMachineFSM().run()

运行与验证

运行方式:将代码保存为 vending_machine.py,在终端执行 python vending_machine.py。

验证路径(按顺序输入以下命令):

insert_coin 5
select A1
insert_coin 2
select B1
select A2
cancel
exit

预期输出与判定标准:

  • 投币5元后状态为COIN_INSERTED,余额5元;
  • 选择A1(可乐3元)后出货,找零2元,回到IDLE;
  • 投币2元后选择B1(薯片5元),提示余额不足,状态仍为COIN_INSERTED;
  • 选择A2(雪碧缺货),提示缺货并退还2元,回到IDLE;
  • cancel时提示无交易可取消;
  • exit退出程序。

若以上每一步的输出与状态迁移符合代码逻辑,且无异常报错,即判定实现通过。

状态迁移图

IDLE COIN_INSERTED DISPENSE CHANGE insert_coin select(有货且余额足) 余额>0 找零完成 cancel / 缺货 / 余额不足 cancel(退币)

图中虚线椭圆表示瞬时状态(DISPENSE、CHANGE),它们不等待外部事件,完成动作后自动迁移。IDLE状态下cancel不产生迁移,仅提示无交易。