Go语言基于interface实现依赖注入的代码问题咨询
Go 基于interface实现依赖注入的正确方案
问题场景
尝试通过Go语言的interface机制实现依赖注入,定义了Shooter接口,约定方法签名如下:
type Shooter interface { Spec(ev v1alpha1.Ev) (v1beta1.Shoot, error) } type Project struct { Name string }
针对该接口分别实现了azure、gcp两个场景的逻辑,实现示例:
func (n Project) Spec(ev v1alpha1.Ev) (v1beta1.Shoot, error) { var shoot = v1beta1.Shoot{} fmt.Println(shoot, ev) return shoot, nil }
在上层包中需要根据场景动态获取对应实现,最初编写的逻辑无法正常运行,错误写法片段如下:
func kind(ev v1alpha1.Ev, namespace string) (v1beta1.Shoot, error) { var shoot v1beta1.Shoot var e error switch ev.Spec.Infrastructure.Type { case "gcp": project := gcp.Project{Name: namespace} shoot, e = project.Spec(ev) if e != nil { return v1beta1.Shoot{}, e }
问题根因
该写法没有遵循面向接口编程的原则,分支逻辑直接耦合了具体结构体的初始化和方法调用:
- 重复逻辑冗余:每个云厂商分支都要写一遍方法调用、错误判断的代码
- 扩展性差:新增实现时必须修改业务逻辑代码,不符合开闭原则
- 容易触发编译/运行错误:如果不同包下结构体的方法接收者类型不匹配,很容易出现接口未实现的问题
正确实现步骤
1. 抽离实例工厂方法
把「根据基础设施类型返回对应实现」的逻辑抽成独立工厂,所有返回值统一使用Shooter接口类型承接,不暴露具体实现细节:
func NewShooter(infraType string, namespace string) (Shooter, error) { switch infraType { case "gcp": return gcp.Project{Name: namespace}, nil case "azure": return azure.Project{Name: namespace}, nil default: return nil, fmt.Errorf("unsupported infrastructure type: %s", infraType) } }
注意:如果你的
Spec方法是指针接收者(即方法定义为func (n *Project) Spec(...)),这里需要返回结构体指针,例如return &gcp.Project{Name: namespace}, nil,否则会报类型不满足接口的编译错误。
2. 上层逻辑只面向接口编程
业务逻辑中只依赖Shooter接口,不需要感知具体是哪个云厂商的实现,统一调用接口方法即可:
func kind(ev v1alpha1.Ev, namespace string) (v1beta1.Shoot, error) { shooter, err := NewShooter(ev.Spec.Infrastructure.Type, namespace) if err != nil { return v1beta1.Shoot{}, err } return shooter.Spec(ev) }
扩展说明
后续新增其他云厂商支持时,只需要两步:
- 在对应厂商的包下定义
Project结构体,实现Spec方法满足Shooter接口约定 - 在
NewShooter工厂方法的switch分支中新增对应类型的返回逻辑
上层业务代码不需要做任何修改,完全符合依赖注入的解耦要求。
内容的提问来源于stack exchange,提问作者PJEM
相关产品推荐
相关产品推荐

