英文:
Dependency injection using go interface
问题
我正在尝试使用一些依赖注入,通过Go接口来实现,这是在阅读一些相关文档后得出的结论。
我有两个方法应该实现同一个接口:
type Shooter interface {
Spec(ev v1alpha1.Ev) (v1beta1.Shoot, error)
}
type Project struct {
Name string
}
具体的实现在这里:
例如:
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
}
但是它不起作用,有任何想法如何正确实现吗?
英文:
Im trying to use some dependency injection via go interface after reading some docs about it.
I’ve two methods which should implement one interface
type Shooter interface {
Spec(ev v1alpha1.Ev) (v1beta1.Shoot, error)
}
type Project struct {
Name string
}
https://github.com/JennyMet/testint/blob/master/internal/infra/common.go#L8
The concrete implementation is here
https://github.com/JennyMet/testint/blob/master/internal/infra/azure/azure.go#L13
https://github.com/JennyMet/testint/blob/master/internal/infra/gcp/gcp.go#L13
e.g.
func (n Project) Spec(ev v1alpha1.Ev) (v1beta1.Shoot, error) {
var shoot = v1beta1.Shoot{}
fmt.Println(shoot, ev)
return shoot, nil
}
Now I want to get the specific implementation in package above and I tried the following
https://github.com/JennyMet/testint/blob/master/internal/infra/provider.go#L16
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
}
But it doesn’t works, any ideas how can I do it right?
答案1
得分: 1
但它不起作用
没有明确指定,但我假设依赖注入不起作用。
注入需要至少两种不同的实体类型。一个是被注入的实体,另一个是接收注入的目标。
在你的情况下,你只有一个 - 在gcp
和azure
中实现的注入。
你需要添加一个包含被注入接口的目标:
type Target struct {
Specer Shooter
}
func (t *Target) DoWork() {
// 在这里,你可以使用t.Specer.Spec(),而不需要知道具体的实现细节
}
现在你可以使用依赖注入创建Target
:
func NewTarget(specer Shooter) *Target{
return &Target{
Specer: specer,
}
}
你的代码通常在应用程序启动时非常接近组合根中决定所有使用的类型并进行注入(调用NewTarget()
)。
更新:
接口也可以注入到函数中。这也是依赖注入:
func kind(ev v1alpha1.Ev, namespace string, specer Shooter) (v1beta1.Shoot, error) {
...
shoot, e = specer.Spec(ev)
...
}
这是一种称为服务定位器的模式的替代方法:
func kind(ev v1alpha1.Ev, namespace string) (v1beta1.Shoot, error) {
...
specer := factory.GetSpecer()
shoot, e = specer.Spec(ev)
...
}
服务定位器经常被称为反模式。
英文:
> But it doesn’t works
It is not specified, but I assume that Dependency Injection does not work.
Injection requires at least two different entity types. One is injected and the second on is a target receiving that injection.
In your case, you have only one - Injection implemented in two forms gcp
and azure
.
You need to add a target that contains injected interface:
type Target struct {
Specer Shooter
}
func (t *Target) DoWork() {
// here you can use t.Specer.Spec() without knowing implementation details
}
Now you can create Target
using DI:
func NewTarget(specer Shooter) *Target{
return &Target{
Specer: specer,
}
}
Your code typically decides on all used types and injects them (calling NewTarget()
) very close to application start in Composition Root
Update:
Interface is also can be injected into function. It is also Dependency Injection:
func kind(ev v1alpha1.Ev, namespace string, specer Shooter) (v1beta1.Shoot, error) {
...
shoot, e = specer.Spec(ev)
...
}
That is an alternative to a pattern called Service Locator:
func kind(ev v1alpha1.Ev, namespace string) (v1beta1.Shoot, error) {
...
specer := factory.GetSpecer()
shoot, e = specer.Spec(ev)
...
}
Service Locator often referred as antipattern.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论