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退出程序。
若以上每一步的输出与状态迁移符合代码逻辑,且无异常报错,即判定实现通过。
状态迁移图
图中虚线椭圆表示瞬时状态(DISPENSE、CHANGE),它们不等待外部事件,完成动作后自动迁移。IDLE状态下cancel不产生迁移,仅提示无交易。