别 if/else 地狱!用 Go 状态模式写出“会思考”的代码

Golang

“我写了个订单系统,结果 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 种状态:pendingpaidshippeddelivered,还能 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! 🥷✨


0
0
0
0
关于作者
关于作者

文章

0

获赞

0

收藏

0

相关资源
云原生机器学习系统落地和实践
机器学习在字节跳动有着丰富业务场景:推广搜、CV/NLP/Speech 等。业务规模的不断增大对机器学习系统从用户体验、训练效率、编排调度、资源利用等方面也提出了新的挑战,而 Kubernetes 云原生理念的提出正是为了应对这些挑战。本次分享将主要介绍字节跳动机器学习系统云原生化的落地和实践。
相关产品
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论