“我写了个订单系统,结果 if/else 比订单还多……”
—— 某位深夜加班的 Go 新手
你有没有写过这样的代码?
if status == "pending" {
// ...
} else if status == "paid" {
// ...
} else if status == "shipped" {
// ...
} else if status == "delivered" {
// ...
} else if status == "cancelled" {
// ...
}
每加一个新状态,就要改所有方法。
想加个“退货”?先准备好咖啡,因为你要改 5 个函数 + 20 行重复判断。
别慌!今天教你一个设计模式界的“忍术”——状态模式(State Pattern),让你的代码从“条件迷宫”变成“状态机大师”。
🤔 为什么 if/else 是个坑?
假设你在做电商系统,订单有 5 种状态:pending → paid → shipped → delivered,还能 cancel。
用字符串 + if/else 写,看似简单,实则:
- 逻辑分散:每个方法都要检查所有状态
- 难以扩展:加个
returned?所有方法重写! - 违反 OCP:开放-封闭原则?不存在的!
这就像让一个客服同时记住 100 个客户的订单规则——迟早崩溃。
🥷 状态模式:让每个状态“自己管自己”
核心思想:把状态的行为封装到独立的结构体里,订单只关心“当前是谁在管事”,不关心具体规则。
第一步:定义状态接口
type OrderState interface {
Pay(*Order) error
Ship(*Order) error
Cancel(*Order) error
Name() string
}
所有状态都必须会回答:“我能付款吗?能发货吗?能取消吗?”
第二步:订单结构体持有“状态对象”
type Order struct {
ID string
state OrderState // 不再是字符串!
}
func (o *Order) Pay() error { return o.state.Pay(o) }
func (o *Order) Ship() error { return o.state.Ship(o) }
func (o *Order) Cancel() error { return o.state.Cancel(o) }
func (o *Order) Status() string { return o.state.Name() }
订单现在像个“甩手掌柜”:具体干啥?问当前状态!
第三步:实现各个状态
🟡 Pending(待支付)
type PendingState struct{}
func (s PendingState) Name() string { return "pending" }
func (s PendingState) Pay(o *Order) error {
fmt.Println("✅ 付款成功!")
o.state = PaidState{} // 切换状态!
return nil
}
func (s PendingState) Ship(*Order) error {
return errors.New("❌ 请先付款")
}
func (s PendingState) Cancel(o *Order) error {
fmt.Println("🚫 订单已取消")
o.state = CancelledState{}
return nil
}
🟢 Paid(已付款)
type PaidState struct{}
func (s PaidState) Name() string { return "paid" }
func (s PaidState) Pay(*Order) error {
return errors.New("💰 已付过款啦")
}
func (s PaidState) Ship(o *Order) error {
fmt.Println("📦 发货中...")
o.state = ShippedState{}
return nil
}
func (s PaidState) Cancel(o *Order) error {
fmt.Println("💸 退款并取消")
o.state = CancelledState{}
return nil
}
其他状态(Shipped、Delivered、Cancelled)同理,每个只管自己的规则!
第四步:使用它!
func main() {
order := &Order{
ID: "1001",
state: PendingState{}, // 初始状态
}
fmt.Println("当前状态:", order.Status()) // pending
_ = order.Ship() // ❌ 请先付款
_ = order.Pay() // ✅ 付款成功!
_ = order.Pay() // 💰 已付过款啦
_ = order.Ship() // 📦 发货中...
_ = order.Cancel() // ❌ cannot cancel shipped order(需实现)
}
输出:
当前状态: pending
❌ 请先付款
✅ 付款成功!
💰 已付过款啦
📦 发货中...
每个状态“各司其职”,新增状态?只需写一个新 struct,完全不用动 Order 本身!
✅ 状态模式的三大优势
| 问题 | 状态模式怎么解决 |
|---|---|
| if/else 膨胀 | 每个状态独立,逻辑集中 |
| 难以扩展 | 加新状态?只写新 struct |
| 重复判断 | 每个方法只处理当前状态 |
它不是“炫技”,而是让复杂流程变得可维护、可测试、可读。
🚫 什么时候别用?
- 状态很少(比如只有 2 个)
- 状态之间行为几乎一样
- 项目生命周期很短(比如一次性脚本)
简单问题,别上重型武器!
🎯 总结:从“写逻辑”到“建系统”
状态模式不是 Go 特有,但在 Go 的 接口 + 结构体组合 下,它格外优雅。
下次当你看到满屏 if status == ...,不妨停下来问一句:
“能不能让状态自己说话?”
答案往往是:能!而且更清爽。
Happy Coding, State Ninja! 🥷✨
