PJEM
PJEM

Reputation: 667

Dependency injection using go interface

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?

Upvotes: 1

Views: 2376

Answers (1)

Dmitry Harnitski
Dmitry Harnitski

Reputation: 6008

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.

Upvotes: 2

Related Questions