Skip to content

refactor(chatapps): 使用接口模式消除 provider-specific branching #365

@hrygo

Description

@hrygo

🎯 问题描述

chatapps/setup.go:292-302 中存在 provider-specific branching,违反了 Open/Closed 原则:

// 当前实现:硬编码 OpenCode Server 特殊处理
if pCfg.Type == provider.ProviderTypeOpenCodeServer && pc.Engine.WorkDir != "" {
    if pCfg.OpenCode == nil {
        pCfg.OpenCode = &provider.OpenCodeConfig{}
    }
    pCfg.OpenCode.WorkDir = pc.Engine.WorkDir
    logger.Debug("Injected work directory to OpenCode Server config",
        "work_dir", pc.Engine.WorkDir)
}

问题根源

  • Leaky abstraction: setup.go 需要知道特定 provider 类型的内部配置结构
  • Open/Closed violation: 每增加一个需要特殊配置的 provider,都需要修改 setup.go
  • Scalability issue: 如果其他 provider 也需要 work directory,会演变成 switch-case 瑞士军刀

📊 影响范围

当前影响

  • ✅ 功能正常:OpenCode Server work directory 注入工作正常
  • ⚠️ 可维护性降低:架构违反 SOLID 原则
  • ⚠️ 扩展性受限:新 provider 需要修改 setup.go

潜在风险

如果有更多 provider 需要特殊配置(如 Pi, Claude Code),代码会变成:

// 未来可能的反模式
if pCfg.Type == provider.ProviderTypeOpenCodeServer {
    // OpenCode Server 逻辑
} else if pCfg.Type == provider.ProviderTypePi {
    // Pi 逻辑
} else if pCfg.Type == provider.ProviderTypeClaudeCode {
    // Claude Code 逻辑
}
// ... 无限扩展

✅ 建议的解决方案

方案:WorkDirConfigurable 接口

核心思路: 让 provider 自己声明是否需要 work directory,而不是 setup.go 硬编码判断

1. 在 provider/provider.go 中定义接口

// WorkDirConfigurable is an optional interface that providers can implement
// to receive work directory configuration from the engine.
type WorkDirConfigurable interface {
    // SetWorkDir configures the working directory for the provider.
    // This is called during provider initialization before CreateProvider().
    SetWorkDir(workDir string)
}

2. OpenCodeConfig 实现接口

// provider/provider.go
func (c *OpenCodeConfig) SetWorkDir(workDir string) {
    c.WorkDir = workDir
}

3. 重构 setup.go

// chatapps/setup.go - 简化后的通用实现
if pc.Engine.WorkDir != "" {
    // Try to inject work directory if provider supports it
    if configurable, ok := prv.(provider.WorkDirConfigurable); ok {
        configurable.SetWorkDir(pc.Engine.WorkDir)
        logger.Debug("Injected work directory to provider config",
            "provider", pCfg.Type,
            "work_dir", pc.Engine.WorkDir)
    }
}

优势

  • Open/Closed: 新增 provider 无需修改 setup.go
  • Interface segregation: 可选接口,不影响不需要的 provider
  • Single responsibility: setup.go 只负责调用接口,不关心具体实现
  • Extensibility: 未来其他 provider 可轻松实现该接口

🔄 迁移路径

Phase 1: 准备(Non-breaking)

  1. 添加 WorkDirConfigurable 接口定义
  2. OpenCodeConfig 实现该接口
  3. 保留旧的 if 分支作为 fallback

Phase 2: 重构(Backward compatible)

  1. setup.go 中添加接口调用逻辑
  2. 移除旧的 provider-specific 分支
  3. 添加单元测试验证接口行为

Phase 3: 验证

  1. 运行完整测试套件
  2. 验证 OpenCode Server 仍然正常工作
  3. 确认其他 provider 不受影响

🧪 验收标准

  • WorkDirConfigurable 接口定义在 provider/provider.go
  • OpenCodeConfig 实现该接口
  • setup.go 使用接口替代硬编码判断
  • 单元测试覆盖接口行为
  • 所有现有测试通过
  • OpenCode Server work directory 功能正常

📚 参考资料

相关代码

  • chatapps/setup.go:292-302 - 当前的 provider-specific branching
  • provider/provider.go:198-224 - OpenCodeConfig 定义
  • provider/transport_http.go:26 - HTTPTransport.workDir 字段

设计原则


优先级: Low (P3) - 不影响当前功能,属于代码质量改进
工作量: Medium (1-3 days) - 接口设计 + 重构 + 测试
标签: type/enhancement, priority/low, size/medium

Metadata

Metadata

Assignees

No one assigned

    Labels

    priority/low🔵 P3 - 小问题、nice-to-have 改进size/medium📏 Medium - 1-3 天工作量type/enhancement💪 Enhancement - 改进现有功能

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions