Golang代码演进案例

代码演进之路:从嵌套地狱到模块化设计

在软件开发过程中,我们常常会遇到一些"丑陋"的代码。这些代码可能功能完整,但结构混乱、难以维护。本文将通过一个Kubernetes部署更新策略的解析函数,展示代码如何从最初的嵌套结构,逐步演进到清晰模块化的设计。

第一阶段:原始嵌套代码

最初的代码实现是一个典型的嵌套条件结构,所有逻辑都集中在一个大函数中:

func (i *Inference) parseUpdateStrategy(ctx context.Context, task *entity.TaskInfoView, subTasks []*entity.SubTaskInfoView) error {
    if i.deployment != nil && task.ExtraConfiguration.UpdateStrategy != nil {
        updateType, ok := task.ExtraConfiguration.UpdateStrategy["type"]
        if ok {
            if updateType == "recreate" {
                log.Debug(ctx, "策略类型为recreate")
                i.deployment.Spec.Strategy.Type = appv1.RecreateDeploymentStrategyType
                i.deployment.Spec.Strategy.RollingUpdate = nil
            } else if updateType == "rollingUpdate" {
                log.Debug(ctx, "策略类型为rollingUpdate")
                i.deployment.Spec.Strategy.Type = appv1.RollingUpdateDeploymentStrategyType
                i.deployment.Spec.Strategy.RollingUpdate = &appv1.RollingUpdateDeployment{
                    MaxUnavailable: func() *intstr.IntOrString {
                        if val := task.ExtraConfiguration.UpdateStrategy["maxUnavailable"]; val != "" {
                            parsed := intstr.Parse(val)
                            log.Debug(ctx, fmt.Sprintf("maxUnavailable解析成功: %+v", parsed))
                            return &parsed
                        }
                        return nil
                    }(),
                    MaxSurge: func() *intstr.IntOrString {
                        if val := task.ExtraConfiguration.UpdateStrategy["maxSurge"]; val != "" {
                            parsed := intstr.Parse(val)
                            log.Debug(ctx, fmt.Sprintf("maxSurge解析成功: %+v", parsed))
                            return &parsed
                        }
                        return nil
                    }(),
                }
            }
        } else {
            log.Error(ctx, "更新策略类型为空")
        }
    } else {
        if i.deployment == nil {
            log.Error(ctx, "deployment对象为空")
        }
        if task.ExtraConfiguration.UpdateStrategy == nil {
            log.Error(ctx, "更新策略配置为空")
        }
    }

    return nil
}

这段代码存在几个明显问题:

  1. 多层嵌套的if-else结构,可读性差
  2. 重复的配置解析逻辑
  3. 缺少明确的错误处理机制
  4. 所有逻辑耦合在一个函数中,难以扩展
第二阶段:函数拆分与模块化

第一次重构时,我们采用了简单的函数拆分方法:

func (i *Inference) parseUpdateStrategy(ctx context.Context, task *entity.TaskInfoView, subTasks []*entity.SubTaskInfoView) error {
    // 参数校验
    if i.deployment == nil {
        return fmt.Errorf("deployment对象为空")
    }
    if task.ExtraConfiguration.UpdateStrategy == nil {
        return fmt.Errorf("更新策略配置为空")
    }

    // 获取策略类型
    updateType, ok := task.ExtraConfiguration.UpdateStrategy["type"]
    if !ok {
        return fmt.Errorf("更新策略类型为空")
    }

    // 应用对应的更新策略
    log.Debug(ctx, fmt.Sprintf("应用更新策略: %s", updateType))
    switch updateType {
    case "recreate":
        return i.applyRecreateStrategy(ctx, task.ExtraConfiguration.UpdateStrategy)
    case "rollingUpdate":
        return i.applyRollingUpdateStrategy(ctx, task.ExtraConfiguration.UpdateStrategy)
    default:
        return fmt.Errorf("不支持的更新策略类型: %s", updateType)
    }
}

// 应用重建策略
func (i *Inference) applyRecreateStrategy(ctx context.Context, config map[string]string) error {
    i.deployment.Spec.Strategy.Type = appv1.RecreateDeploymentStrategyType
    i.deployment.Spec.Strategy.RollingUpdate = nil
    return nil
}

// 应用滚动更新策略
func (i *Inference) applyRollingUpdateStrategy(ctx context.Context, config map[string]string) error {
    i.deployment.Spec.Strategy.Type = appv1.RollingUpdateDeploymentStrategyType
    i.deployment.Spec.Strategy.RollingUpdate = &appv1.RollingUpdateDeployment{
        MaxUnavailable: parseUpdateConfigValue(ctx, config, "maxUnavailable"),
        MaxSurge:       parseUpdateConfigValue(ctx, config, "maxSurge"),
    }
    return nil
}

// 解析更新配置值
func parseUpdateConfigValue(ctx context.Context, config map[string]string, key string) *intstr.IntOrString {
    if val, exists := config[key]; exists && val != "" {
        parsed := intstr.Parse(val)
        log.Debug(ctx, fmt.Sprintf("%s解析成功: %+v", key, parsed))
        return &parsed
    }
    return nil
}

这次重构带来了以下改进:

  1. 提取了不同策略的处理逻辑到单独的方法
  2. 统一了参数校验,使用错误返回代替日志
  3. 抽取了配置解析的公共逻辑
  4. 使用switch语句代替嵌套if-else,结构更清晰
第三阶段:工厂模式与策略模式

随着系统复杂度增加,我们需要更灵活的设计:

// 更新策略接口
type UpdateStrategy interface {
    Apply(deployment *appv1.Deployment, config map[string]string) error
}

// 重建策略实现
type RecreateStrategy struct{}

func (s *RecreateStrategy) Apply(deployment *appv1.Deployment, config map[string]string) error {
    deployment.Spec.Strategy.Type = appv1.RecreateDeploymentStrategyType
    deployment.Spec.Strategy.RollingUpdate = nil
    return nil
}

// 滚动更新策略实现
type RollingUpdateStrategy struct{}

func (s *RollingUpdateStrategy) Apply(deployment *appv1.Deployment, config map[string]string) error {
    deployment.Spec.Strategy.Type = appv1.RollingUpdateDeploymentStrategyType
    deployment.Spec.Strategy.RollingUpdate = &appv1.RollingUpdateDeployment{
        MaxUnavailable: parseUpdateConfigValue(config, "maxUnavailable"),
        MaxSurge:       parseUpdateConfigValue(config, "maxSurge"),
    }
    return nil
}

// 配置解析工具函数
func parseUpdateConfigValue(config map[string]string, key string) *intstr.IntOrString {
    if val, exists := config[key]; exists && val != "" {
        parsed := intstr.Parse(val)
        return &parsed
    }
    return nil
}

// 更新策略工厂
type UpdateStrategyFactory struct{}

func (f *UpdateStrategyFactory) CreateStrategy(strategyType string) (UpdateStrategy, error) {
    switch strategyType {
    case "recreate":
        return &RecreateStrategy{}, nil
    case "rollingUpdate":
        return &RollingUpdateStrategy{}, nil
    default:
        return nil, fmt.Errorf("不支持的策略类型: %s", strategyType)
    }
}

// 重构后的解析函数
func (i *Inference) parseUpdateStrategy(ctx context.Context, task *entity.TaskInfoView, subTasks []*entity.SubTaskInfoView) error {
    // 参数校验
    if i.deployment == nil {
        return fmt.Errorf("deployment对象为空")
    }
    if task.ExtraConfiguration.UpdateStrategy == nil {
        return fmt.Errorf("更新策略配置为空")
    }

    // 获取策略类型
    updateType, ok := task.ExtraConfiguration.UpdateStrategy["type"]
    if !ok {
        return fmt.Errorf("更新策略类型为空")
    }

    // 创建并应用策略
    factory := &UpdateStrategyFactory{}
    strategy, err := factory.CreateStrategy(updateType)
    if err != nil {
        return err
    }

    log.Debug(ctx, fmt.Sprintf("应用更新策略: %s", updateType))
    return strategy.Apply(i.deployment, task.ExtraConfiguration.UpdateStrategy)
}

这次重构的优势:

  1. 完全解耦了策略实现与调用逻辑
  2. 引入了策略模式,便于添加新的策略类型
  3. 工厂模式封装了对象创建过程
  4. 提高了代码的可测试性和可扩展性

演进总结

从这个简单的函数演进过程,我们可以总结出一些代码优化的经验:

  1. 从小处着手:先进行简单的函数拆分,提高可读性
  2. 提取公共逻辑:识别重复代码并抽取为工具函数
  3. 应用设计模式:当需求复杂度增加时,引入适当的设计模式
  4. 持续重构:代码质量是持续改进的过程,不是一次性完成的

通过这种渐进式的重构方法,我们不仅提高了代码质量,还减少了引入新bug的风险。最终得到的代码结构清晰、易于维护,并且能够更好地适应未来的变化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值