golang中是否有更好的依赖注入模式?

时间:2017-01-27 17:48:59

标签: go interface

鉴于此代码:

package main

import (
    "fmt"
)

type datstr string

type Guy interface {
   SomeDumbGuy() string
}

func (d *datstr) SomeDumbGuy() string {
  return "some guy"
}

func someConsumer(g Guy) {
  fmt.Println("Hello, " + g.SomeDumbGuy())
}

func main() {
    var d datstr
    someConsumer(&d)
}

方式完成的组件接线是否正确地将依赖关系连接在一起?好像我在代码中使用了这一点。有没有比这更好的共同模式,还是我在思考它?

6 个答案:

答案 0 :(得分:10)

最佳做法是不使用DI库。

Go是一种易于理解的简单语言。
DI库/框架将使您远离它(并在某种程度上使DI 魔术

您所做的几乎是正确的。

来源:https://youtu.be/PTE4VJIdHPg?list=WL&t=601

Peter Bourgon的博客中也提供了相同的解释:https://peter.bourgon.org/go-for-industrial-programming/

另一个blog强调了我们如何手动设置DI。

答案 1 :(得分:7)

是的,facebookgo注入库允许您接受注入的成员并为您连接图表。

代码:https://github.com/facebookgo/inject

文档:https://godoc.org/github.com/facebookgo/inject

以下是文档中的代码示例:

package main

import (
    "fmt"
    "net/http"
    "os"

    "github.com/facebookgo/inject"
)

// Our Awesome Application renders a message using two APIs in our fake
// world.
type HomePlanetRenderApp struct {
    // The tags below indicate to the inject library that these fields are
    // eligible for injection. They do not specify any options, and will
    // result in a singleton instance created for each of the APIs.

    NameAPI   *NameAPI   `inject:""`
    PlanetAPI *PlanetAPI `inject:""`
}

func (a *HomePlanetRenderApp) Render(id uint64) string {
    return fmt.Sprintf(
        "%s is from the planet %s.",
        a.NameAPI.Name(id),
        a.PlanetAPI.Planet(id),
    )
}

// Our fake Name API.
type NameAPI struct {
    // Here and below in PlanetAPI we add the tag to an interface value.
    // This value cannot automatically be created (by definition) and
    // hence must be explicitly provided to the graph.

    HTTPTransport http.RoundTripper `inject:""`
}

func (n *NameAPI) Name(id uint64) string {
    // in the real world we would use f.HTTPTransport and fetch the name
    return "Spock"
}

// Our fake Planet API.
type PlanetAPI struct {
    HTTPTransport http.RoundTripper `inject:""`
}

func (p *PlanetAPI) Planet(id uint64) string {
    // in the real world we would use f.HTTPTransport and fetch the planet
    return "Vulcan"
}

func main() {
    // Typically an application will have exactly one object graph, and
    // you will create it and use it within a main function:
    var g inject.Graph

    // We provide our graph two "seed" objects, one our empty
    // HomePlanetRenderApp instance which we're hoping to get filled out,
    // and second our DefaultTransport to satisfy our HTTPTransport
    // dependency. We have to provide the DefaultTransport because the
    // dependency is defined in terms of the http.RoundTripper interface,
    // and since it is an interface the library cannot create an instance
    // for it. Instead it will use the given DefaultTransport to satisfy
    // the dependency since it implements the interface:
    var a HomePlanetRenderApp
    err := g.Provide(
        &inject.Object{Value: &a},
        &inject.Object{Value: http.DefaultTransport},
    )
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    // Here the Populate call is creating instances of NameAPI &
    // PlanetAPI, and setting the HTTPTransport on both to the
    // http.DefaultTransport provided above:
    if err := g.Populate(); err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    // There is a shorthand API for the simple case which combines the
    // three calls above is available as inject.Populate:
    //
    //   inject.Populate(&a, http.DefaultTransport)
    //
    // The above API shows the underlying API which also allows the use of
    // named instances for more complex scenarios.

    fmt.Println(a.Render(42))

}

答案 2 :(得分:3)

Google的Wire看起来很有前途。有一些关于它的文章:

答案 3 :(得分:1)

您还应该尝试Dargo,这是新功能,但具有Facebook用户所没有的一些功能。代码是here

这里是一个例子:

在此示例中,名为SimpleService的服务将注入记录器。记录器本身是与创建方法绑定的dargo服务。该创建方法如下所示:

func newLogger(ioc.ServiceLocator, ioc.Descriptor) (interface{}, error) {
    return logrus.New(), nil
}

SimpleService的绑定将提供应用于实现接口的结构。该结构具有一个字段,该字段注有“注入”,后跟要注入的服务的名称。这是接口以及用于实现它的结构:

type SimpleService interface {
    // CallMe logs a message to the logger!
    CallMe()
}

// SimpleServiceData is a struct implementing SimpleService
type SimpleServiceData struct {
    Log *logrus.Logger `inject:"LoggerService_Name"`
}

// CallMe implements the SimpleService method
func (ssd *SimpleServiceData) CallMe() {
    ssd.Log.Info("This logger was injected!")
}

记录器服务和SimpleService都绑定到ServiceLocator中。这通常是在程序开始时完成的:

locator, err := ioc.CreateAndBind("InjectionExampleLocator", func(binder ioc.Binder) error {
        // Binds SimpleService by providing the structure
        binder.Bind("SimpleService", SimpleServiceData{})

        // Binds the logger service by providing the creation function 
        binder.BindWithCreator("LoggerService_Name", newLogger).InScope(ioc.PerLookup)

        return nil
    })

返回的定位符可用于查找SimpleService服务。 SimpleService绑定到Singleton范围(默认范围),这意味着它将仅在第一次查找或注入时创建,并且永远不会再次创建。另一方面,LoggerService在PerLookup范围内,这意味着每次注入或查找LoggerService时,都会创建一个新的。

这是使用查找服务的代码:

raw, err := locator.GetDService("SimpleService")
if err != nil {
    return err
}

ss, ok := raw.(SimpleService)
if !ok {
    return fmt.Errorf("Invalid type for simple service %v", ss)
}

ss.CallMe()

支持任何注入深度(ServiceA可以取决于ServiceB,而ServiceB取决于ServiceC等等)。服务还可以依赖于任意数量的服务(ServiceA可以依赖于服务D,E和F等)。但是,服务不能具有循环依赖性。

答案 4 :(得分:1)

如果您仍然想为Go寻找一个使用最小反射的DI库,那么我将其命名为axon。它基于Google的Guice,因此,如果您来自Java世界(例如我自己),那么它应该很好地适合您的期望。

我已经在Web服务器中使用了它,它没有任何问题,并且速度足够快,不会对在CLI中使用它产生任何影响。

答案 5 :(得分:0)

Uber的Dig非常棒。这是一篇很棒的博客文章:Dependency Injection in Go