2. 自动售货机状态机
任务目标
实现一个自动售货机状态机,支持投币、选择商品、出货与找零操作。状态包括:空闲(IDLE)、接收硬币(COIN)、选择商品(SELECT)、出货(DISPENSE)、找零(CHANGE)。核心规则:投入金额≥商品价格才可出货;出货后若有多余金额则进入找零状态,找零完成后回到空闲。
状态定义与迁移
| 当前状态 | 事件 | 条件 | 动作 | 下一状态 |
|---|---|---|---|---|
| IDLE | 投币(amount) | amount>0 | 累计金额 | COIN |
| COIN | 投币(amount) | amount>0 | 累计金额 | COIN |
| COIN | 选择商品(code) | 商品存在 | 记录商品 | SELECT |
| SELECT | 确认购买 | 余额≥价格 | 扣款、出货 | DISPENSE |
| SELECT | 取消 | — | 退还全部金额 | CHANGE |
| DISPENSE | 出货完成 | 余额>0 | 计算找零 | CHANGE |
| DISPENSE | 出货完成 | 余额==0 | — | IDLE |
| CHANGE | 找零完成 | — | 清零 | IDLE |
完整实现(Python)
class VendingMachine:
def __init__(self):
self.state = "IDLE"
self.balance = 0
self.selected = None
self.products = {"A1": 10, "B2": 15, "C3": 20} # 商品代码:价格
def insert_coin(self, amount):
if amount <= 0:
return "无效金额"
if self.state in ("IDLE", "COIN"):
self.balance += amount
self.state = "COIN"
return f"已投币{amount}元,余额{self.balance}元"
return f"当前状态[{self.state}]不允许投币"
def select_product(self, code):
if self.state != "COIN":
return f"当前状态[{self.state}]不能选择商品"
if code not in self.products:
return "商品不存在"
self.selected = code
self.state = "SELECT"
return f"已选择{code},价格{self.products[code]}元"
def confirm(self):
if self.state != "SELECT":
return f"当前状态[{self.state}]不能确认购买"
price = self.products[self.selected]
if self.balance < price:
return f"余额不足,还需{price - self.balance}元"
self.balance -= price
self.state = "DISPENSE"
return f"出货:{self.selected}"
def dispense_done(self):
if self.state != "DISPENSE":
return f"当前状态[{self.state}]无出货"
if self.balance > 0:
change = self.balance
self.balance = 0
self.selected = None
self.state = "CHANGE"
return f"出货完成,找零{change}元"
self.selected = None
self.state = "IDLE"
return "出货完成,无找零"
def change_done(self):
if self.state != "CHANGE":
return f"当前状态[{self.state}]无找零"
self.state = "IDLE"
return "找零完成,回到空闲"
def cancel(self):
if self.state == "SELECT":
refund = self.balance
self.balance = 0
self.selected = None
self.state = "CHANGE"
return f"已取消,退还{refund}元"
return f"当前状态[{self.state}]不能取消"
运行与验证
# 测试脚本
vm = VendingMachine()
print(vm.insert_coin(20)) # 投20元
print(vm.select_product("A1")) # 选10元商品
print(vm.confirm()) # 出货A1
print(vm.dispense_done()) # 找零10元
print(vm.change_done()) # 回到空闲
print("---")
print(vm.insert_coin(5)) # 投5元
print(vm.select_product("B2")) # 选15元商品
print(vm.confirm()) # 余额不足
print(vm.cancel()) # 取消,退5元
print(vm.change_done())
预期输出(由代码逻辑直接推出):
已投币20元,余额20元
已选择A1,价格10元
出货:A1
出货完成,找零10元
找零完成,回到空闲
---
已投币5元,余额5元
已选择B2,价格15元
余额不足,还需10元
已取消,退还5元
找零完成,回到空闲
验证方法:将上述代码保存为 vending.py,执行 python vending.py。观察输出是否与预期一致,且最终状态回到 IDLE。可额外测试:投币后直接取消(应提示当前状态不能取消)、连续投币累加余额、选择不存在的商品码等边界场景。
关键说明
- 状态迁移完全由
self.state控制,非法操作返回提示但不改变状态。 - 找零金额在
dispense_done()中计算并清零余额,change_done()仅负责状态复位。 - 商品价格表
self.products可扩展,不影响状态机逻辑。