首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Go实战训练学习笔记----循环与条件语句实战,简化业务逻辑代码

Go实战训练学习笔记----循环与条件语句实战,简化业务逻辑代码

原创
作者头像
用户12339161
修改2026-05-29 16:50:34
修改2026-05-29 16:50:34
170
举报

业务代码里 80% 是循环和条件判断,但多数人写出来的是"面条代码"。本文用 Go 实战,讲清楚怎么把嵌套 if、冗长 switch、混乱循环,一步步简化成清晰、可维护的逻辑。


一、先看一个真实的"反面教材"

这是一段常见的订单处理逻辑:

代码语言:javascript
复制
gofunc ProcessOrder(order *Order) error {
    if order != nil {
        if order.Status == "pending" {
            if order.Amount > 0 {
                if order.User != nil {
                    if order.User.Level == "vip" {
                        order.Discount = 0.8
                    } else {
                        if order.User.Level == "new" {
                            order.Discount = 0.9
                        } else {
                            order.Discount = 1.0
                        }
                    }
                    return nil
                } else {
                    return errors.New("user is nil")
                }
            } else {
                return errors.New("invalid amount")
            }
        } else {
            return errors.New("invalid status")
        }
    } else {
        return errors.New("order is nil")
    }
}

5 层嵌套,7 个判断,阅读成本极高。 这不是个例,这是大多数业务代码的日常。

下面逐一拆解简化手段。


二、第一招:用"卫语句"(Early Return)消灭嵌套

卫语句的核心:不满足条件就立刻返回,让主逻辑保持在最外层。

代码语言:javascript
复制
gofunc ProcessOrder(order *Order) error {
    // 卫语句:逐层拦截,不满足就退出
    if order == nil {
        return errors.New("order is nil")
    }
    if order.Status != "pending" {
        return errors.New("invalid status")
    }
    if order.Amount <= 0 {
        return errors.New("invalid amount")
    }
    if order.User == nil {
        return errors.New("user is nil")
    }

    // ✅ 主逻辑:没有任何嵌套,一目了然
    switch order.User.Level {
    case "vip":
        order.Discount = 0.8
    case "new":
        order.Discount = 0.9
    default:
        order.Discount = 1.0
    }
    return nil
}

效果对比

指标

修改前

修改后

嵌套层数

5 层

0 层

可读性

需要缩进追踪

线性阅读

新增校验

要找地方塞 if

顶部加一行卫语句

原则:每个函数的主逻辑,缩进不超过一层。 做不到,说明该拆函数了。


三、第二招:用 Map 替代冗长的 if-else / switch

当条件判断的本质是"映射"时,if-else 是最差的选择。

反面:用 if-else 做折扣计算

代码语言:javascript
复制
govar discount float64
if level == "vip" {
    discount = 0.8
} else if level == "new" {
    discount = 0.9
} else if level == "member" {
    discount = 0.95
} else {
    discount = 1.0
}

正面:用 Map 做查找

代码语言:javascript
复制
govar discounts = map[string]float64{
    "vip":    0.8,
    "new":    0.9,
    "member": 0.95,
}

discount := discounts[level]
if discount == 0 {
    discount = 1.0 // 默认值
}

更简洁的写法(Go 1.21+):

代码语言:javascript
复制
godiscount := map[string]float64{
    "vip": 0.8, "new": 0.9, "member": 0.95,
}[level]

if discount == 0 {
    discount = 1.0
}

为什么 Map 更好?

对比项

if-else

Map

增加新类型

改代码逻辑

加一行数据

性能

O(n) 逐条判断

O(1) 哈希查找

可测试性

要覆盖所有分支

测数据就行

可读性

分支多了就乱

一目了然


四、第三招:用 for range + 函数式风格替代手动循环

反面:手动索引循环

代码语言:javascript
复制
go// 场景:计算所有 VIP 用户的订单总额
var total float64
for i := 0; i < len(orders); i++ {
    if orders[i].User.Level == "vip" {
        total += orders[i].Amount
    }
}

正面:range + 过滤 + 聚合

代码语言:javascript
复制
gototal := 0.0
for _, o := range orders {
    if o.User.Level == "vip" {
        total += o.Amount
    }
}

再进一步,用函数封装:

代码语言:javascript
复制
gofunc Sum(orders []Order, filter func(Order) bool) float64 {
    var total float64
    for _, o := range orders {
        if filter(o) {
            total += o.Amount
        }
    }
    return total
}

// 调用
vipTotal := Sum(orders, func(o Order) bool {
    return o.User.Level == "vip"
})

Go 1.23+ 还可以用 slices 包:

代码语言:javascript
复制
goimport "slices"

vipOrders := slices.DeleteFunc(orders, func(o Order) bool {
    return o.User.Level != "vip"
})

vipTotal := slices.SumFunc(vipOrders, func(o Order) float64 {
    return o.Amount
})

原则:能用 range 就别用索引,能用标准库函数就别自己写循环。


五、第四招:用"表驱动法"替代复杂的条件分支

当一个函数里有大量 if-elseswitch 处理不同类型时,用表驱动重构。

反面:类型分散处理

代码语言:javascript
复制
gofunc HandlePayment(p Payment) error {
    switch p.Type {
    case "alipay":
        return handleAlipay(p)
    case "wechat":
        return handleWechat(p)
    case "bank":
        return handleBank(p)
    case "crypto":
        return handleCrypto(p)
    default:
        return errors.New("unknown type")
    }
}

正面:注册表 + 查表调用

代码语言:javascript
复制
gotype Handler func(Payment) error

var paymentHandlers = map[string]Handler{
    "alipay":  handleAlipay,
    "wechat":  handleWechat,
    "bank":    handleBank,
    "crypto":  handleCrypto,
}

func HandlePayment(p Payment) error {
    handler, ok := paymentHandlers[p.Type]
    if !ok {
        return errors.New("unknown payment type: " + p.Type)
    }
    return handler(p)
}

好处

  • 新增支付方式?一行注册,不改 HandlePayment 逻辑
  • 单元测试?直接测每个 handler,不用走 switch
  • 并行扩展?paymentHandlers 可以从配置文件加载

六、第五招:用策略模式替代嵌套条件

当条件判断不是查数据,而是选行为时,用接口 + 策略模式。

反面:订单定价逻辑

代码语言:javascript
复制
gofunc CalculatePrice(order *Order) float64 {
    if order.User.Level == "vip" {
        if order.Amount > 1000 {
            return order.Amount * 0.7
        }
        return order.Amount * 0.8
    } else if order.User.Level == "new" {
        return order.Amount * 0.9
    }
    return order.Amount
}

正面:策略接口

代码语言:javascript
复制
gotype PricingStrategy interface {
    Calculate(amount float64) float64
}

type VIPStrategy struct{}
func (s VIPStrategy) Calculate(a float64) float64 {
    if a > 1000 {
        return a * 0.7
    }
    return a * 0.8
}

type NewUserStrategy struct{}
func (s NewUserStrategy) Calculate(a float64) float64 {
    return a * 0.9
}

type RegularStrategy struct{}
func (s RegularStrategy) Calculate(a float64) float64 {
    return a
}

var strategyMap = map[string]PricingStrategy{
    "vip":   VIPStrategy{},
    "new":   NewUserStrategy{},
    "regular": RegularStrategy{},
}

func CalculatePrice(order *Order) float64 {
    s := strategyMap[order.User.Level]
    return s.Calculate(order.Amount)
}

核心思想:把"如果是 A 就怎样,如果是 B 就怎样"的逻辑,变成"查表拿策略,策略自己知道怎么做"。


七、第六招:用 select 替代多条件等待循环

当循环的目的是"等某个条件成立"时,别用 for + sleep

反面:轮询等待

代码语言:javascript
复制
gofunc WaitReady(ch chan bool) {
    for {
        select {
        case <-ch:
            return
        default:
            time.Sleep(100 * time.Millisecond)
        }
    }
}

这段代码其实没问题,但如果是多条件等待:

正面:select 多路复用

代码语言:javascript
复制
gofunc WaitAny(ready chan bool, timeout <-chan time.Time) error {
    select {
    case <-ready:
        return nil
    case <-timeout:
        return errors.New("timeout")
    }
}

再复杂一点:

代码语言:javascript
复制
goselect {
case result := <-taskChan:
    return result
case <-ctx.Done():
    return ctx.Err()
case <-time.After(5 * time.Second):
    return errors.New("timeout")
}

原则:能用 select 表述的并发逻辑,别用 for + sleep 硬轮询。


八、简化前后对比总览

技巧

适用场景

简化效果

卫语句

多层前置校验

嵌套 → 线性

Map 替代 if-else

值映射、折扣、状态转换

分支 → 查表

for range

遍历 + 过滤

索引 → 语义化

表驱动法

多类型分发处理

switch → 注册表

策略模式

条件选行为

if-else → 多态

select

并发等待、多条件分支

轮询 → 事件驱动


九、写在最后

循环和条件语句本身不复杂,复杂的是它们嵌套在一起之后的组合爆炸

简化的本质不是"少写代码",而是:

  1. 让主逻辑只有一层缩进(卫语句)
  2. 让分支变成数据(Map / 表驱动)
  3. 让行为变成接口(策略模式)

下次写业务逻辑时,先问自己:这段代码里,有多少个 if 是可以变成一行 Map 查找的? 答案通常比你想的多。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 业务代码里 80% 是循环和条件判断,但多数人写出来的是"面条代码"。本文用 Go 实战,讲清楚怎么把嵌套 if、冗长 switch、混乱循环,一步步简化成清晰、可维护的逻辑。
    • 一、先看一个真实的"反面教材"
    • 二、第一招:用"卫语句"(Early Return)消灭嵌套
    • 三、第二招:用 Map 替代冗长的 if-else / switch
      • 反面:用 if-else 做折扣计算
      • 正面:用 Map 做查找
    • 四、第三招:用 for range + 函数式风格替代手动循环
      • 反面:手动索引循环
      • 正面:range + 过滤 + 聚合
    • 五、第四招:用"表驱动法"替代复杂的条件分支
      • 反面:类型分散处理
      • 正面:注册表 + 查表调用
    • 六、第五招:用策略模式替代嵌套条件
      • 反面:订单定价逻辑
      • 正面:策略接口
    • 七、第六招:用 select 替代多条件等待循环
      • 反面:轮询等待
      • 正面:select 多路复用
    • 八、简化前后对比总览
    • 九、写在最后
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档