如何在Go中实现依赖注入

时间:2016-04-17 12:46:59

标签: go dependency-injection

我将应用程序从Play(Scala)移植到Go,并想知道如何实现依赖项注入。在Scala中我使用了蛋糕模式,而在Go中,我实现了一个DAO接口以及一个Mongo的实现。

下面是我尝试实现一种模式,让我根据需要更改DAO实现(例如测试,不同的数据库等):

1。 entity.go

package models

import (
    "time"
    "gopkg.in/mgo.v2/bson"
)

type (
    Entity struct {
        Id        bson.ObjectId `json:"id,omitempty" bson:"_id,omitempty"`
        CreatedAt time.Time     `json:"createdAt,omitempty" bson:"createdAt,omitempty"`
        LastUpdate time.Time    `json:"lastUpdate,omitempty" bson:"lastUpdate,omitempty"`
    }
)

2。 user.go

package models

import (
    "time"
)

type (
    User struct {
        Entity                  `bson:",inline"`
        Name      string        `json:"name,omitempty" bson:"name,omitempty"`
        BirthDate time.Time     `json:"birthDate,omitempty" bson:"birthDate,omitempty"`
    }
)

第3。 dao.go

package persistence

type (
    DAO interface {
        Insert(entity interface{}) error
        List(result interface{}, sort string) error
        Find(id string, result interface{}) error
        Update(id string, update interface{}) error
        Remove(id string) error
        Close()
    }

    daoFactory func() DAO
)

var (
    New daoFactory
)

4。 mongoDao.go (数据库信息和集合名称是硬编码的,因为它只是一个例子)

package persistence

import (
    "fmt"
    "time"
    "errors"
    "gopkg.in/mgo.v2"
    "gopkg.in/mgo.v2/bson"
    "github.com/fatih/structs"

    "cmd/server/models"
)

type (
    mongoDAO struct{
        session *mgo.Session
    }
)

func NewMongoDAO() DAO {
    dialInfo := &mgo.DialInfo{
        Addrs:      []string{"localhost:27017"},
        Timeout:    60 * time.Second,
        Database:   "test",
    }

    session, err := mgo.DialWithInfo(dialInfo)

    if err != nil {
        panic(err)
    }

    session.SetMode(mgo.Monotonic, true)

    return &mongoDAO{session}
}

func (dao *mongoDAO) Insert(entity interface{}) error {

    doc := entity.(*models.User)
    doc.Id = bson.NewObjectId()
    doc.CreatedAt = time.Now().UTC()
    doc.LastUpdate = time.Now().UTC()

    return dao.session.DB("test").C("users").Insert(doc)
}

func (dao *mongoDAO) List(result interface{}, sort string) error {
    return dao.session.DB("test").C("users").Find(nil).Sort(sort).All(result)
}

func (dao *mongoDAO) Find(id string, result interface{}) error {
    if !bson.IsObjectIdHex(id) {
        return errors.New(fmt.Sprintf("%s is not a valid hex id", id))
    }

    oid := bson.ObjectIdHex(id)

    return dao.session.DB("test").C("users").FindId(oid).One(result)
}

func (dao *mongoDAO) Update(id string, update interface{}) error {
    if !bson.IsObjectIdHex(id) {
        return errors.New(fmt.Sprintf("%s is not a valid hex id", id))
    }

    oid := bson.ObjectIdHex(id)

    doc := update.(*models.User)
    doc.LastUpdate = time.Now().UTC()

    return dao.session.DB("test").C("users").Update(oid, bson.M{"$set": structs.Map(update)})
}

func (dao *mongoDAO) Remove(id string) error {
    if !bson.IsObjectIdHex(id) {
        return errors.New(fmt.Sprintf("%s is not a valid hex id", id))
    }

    oid := bson.ObjectIdHex(id)

    return dao.session.DB("test").C("users").RemoveId(oid)
}

func (dao *mongoDAO) Close() {
    dao.session.Close()
}

func init() {
    New = NewMongoDAO
}

最后,以下是我如何使用上述类型:

5。 userController.go

package controllers

import (
    "net/http"
    "github.com/labstack/echo"

    "cmd/server/models"
    "cmd/server/persistence"
)

type (
    UserController struct {
        dao persistence.DAO
    }
)

func NewUserController(dao persistence.DAO) *UserController {
    return &UserController{dao}
}

func (userController *UserController) CreateUser() echo.HandlerFunc {
    return func(context echo.Context) error {
        user := &models.User{}

        if err := context.Bind(user); err != nil {
            return err
        }

        if err := userController.dao.Insert(user); err != nil {
            return err
        }

        return context.JSON(http.StatusCreated, user)
    }
}

func (userController *UserController) UpdateUser() echo.HandlerFunc {
    return func(context echo.Context) error {
        user := &models.User{}

        if err := context.Bind(user); err != nil {
            return err
        }

        id := context.Param("id")
        if err := userController.dao.Update(id, user); err != nil {
            return err
        }

        return context.JSON(http.StatusOK, user)
    }
}

....

上面的代码很好90%... mongoDao.go中只有方法InsertUpdate的问题,编译器强制我输入输入{{ 1}}到特定类型(entity),但是这使我无法使用适用于所有类型的通用*models.User组件。我该如何解决这个问题?

2 个答案:

答案 0 :(得分:1)

这种概括

DAO interface {
    Insert(entity interface{}) error

看起来过头了 你们都向*models.User断言mongo

doc := entity.(*models.User)

并做

user := &models.User{}
userController.dao.Insert(user)

使用通用DAO接口时。 为什么不准确定义界面?

DAO interface {
    Insert(entity *models.User) error

答案 1 :(得分:1)

如何创建为Entity结构实现的接口?

type Entitier interface {
    GetEntity() *Entity
}

实现只会返回一个指向自身的指针,您现在可以在DAO的InsertUpdate方法中使用它。这还有一个额外的好处,让您在DAO方法的声明中更具体。您现在可以说他们采用interface{}而不是简单地声明他们采用任意Entitier作为参数。

像这样:

func (dao *mongoDAO) Update(id string, update Entitier) error

以下是我的意思的最小完整示例:

http://play.golang.org/p/lpVs_61mfM

希望这会给你一些想法!一旦确定要使用的模式,您可能需要调整Entity / Entitier / GetEntity的命名,以确定样式和清晰度。