Greg
Greg

Reputation: 1024

Dependency injection with interface in go

I have a struct StructDependingOnInterface which depends on MyInterface interface. I'd like the struct Implementation to be injected into bar when StructDependingOnInterface is instantiated.

I have tried doing this with facebookgo/inject library, but it seems like it doesn't work with interfaces. s.bar is always nil.

package main

import (
    "fmt"
    "log"

    "github.com/facebookgo/inject"
)

type MyInterface interface {
    Foo()
}

type Implementation struct{}

func (imp *Implementation) Foo() {
    fmt.Println("Hello")
}

type StructDependingOnInterface struct {
    bar MyInterface `inject:""`
}

func main() {
    var g inject.Graph

    err := g.Provide(
        &inject.Object{Value: &Implementation{}},
    )
    if err != nil {
        log.Fatal(err)
    }

    g.Populate()

    s := &StructDependingOnInterface{}
    s.bar.Foo()
}

Does the language go allows what I'm trying to do ?

Is there an alternative of facebookgo/inject that would fits my need ?

Upvotes: 2

Views: 4036

Answers (2)

John Deng
John Deng

Reputation: 29

hiboot is an cli/web framework that support dependency injection written in Go.

Code: https://github.com/hidevopsio/hiboot

Hiboot Overview
  • Web MVC (Model-View-Controller).
  • Auto Configuration, pre-create instance with properties configs for dependency injection.
  • Dependency injection with struct tag name inject:"" or Constructor func.
Sample Code:
package main

import (
    "github.com/hidevopsio/hiboot/pkg/app/web"
    "github.com/hidevopsio/hiboot/pkg/model"
    "github.com/hidevopsio/hiboot/pkg/starter/jwt"
    "time"
)

// This example shows that token is injected through constructor,
// once you imported "github.com/hidevopsio/hiboot/pkg/starter/jwt",
// token jwt.Token will be injectable.
func main() {
    // create new web application and run it
    web.NewApplication().Run()
}

// PATH: /login
type loginController struct {
    web.Controller

    token jwt.Token
}

type userRequest struct {
    // embedded field model.RequestBody mark that userRequest is request body
    model.RequestBody
    Username string `json:"username" validate:"required"`
    Password string `json:"password" validate:"required"`
}

func init() {
    // Register Rest Controller through constructor newLoginController
    web.RestController(newLoginController)
}

// inject token through the argument token jwt.Token on constructor newLoginController, you may wonder why is token come from
// well, it provided by hiboot starter jwt, see above hiboot link for more details
func newLoginController(token jwt.Token) *loginController {
    return &loginController{
        token: token,
    }
}

// Post /
// The first word of method is the http method POST, the rest is the context mapping
func (c *loginController) Post(request *userRequest) (response model.Response, err error) {
    jwtToken, _ := c.token.Generate(jwt.Map{
        "username": request.Username,
        "password": request.Password,
    }, 30, time.Minute)

    response = new(model.BaseResponse)
    response.SetData(jwtToken)

    return
}

Upvotes: 0

mkopriva
mkopriva

Reputation: 38233

facebookgo/inject should be able to handle interfaces, look at the example in the link and how they use http.RoundTripper and http.DefaultTransport.

First you need to export bar, i.e. changing it to Bar. Then you also need to pass s to Provide for g.Populate to then be able to set Bar.

type StructDependingOnInterface struct {
    Bar MyInterface `inject:""`
}

func main() {
    var g inject.Graph

    s := &StructDependingOnInterface{}
    err := g.Provide(
        &inject.Object{Value: s},
        &inject.Object{Value: &Implementation{}},
    )
    if err != nil {
        log.Fatal(err)
    }

    if err := g.Populate(); err != nil {
         log.Fatal(err)
    }

    s.bar.Foo()
}

Upvotes: 3

Related Questions