在golang有更好的依赖注入模式吗?

ykejflvf  于 2023-02-27  发布在  Go
关注(0)|答案(9)|浏览(158)

给定以下代码:

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)
}

main中完成的将组件连接在一起是将依赖关系连接在一起的正确方式吗?看起来我在代码中已经不再使用这种方式了。是否有比这更好的通用模式,或者是我想多了?

e0uiprwp

e0uiprwp1#

最好的做法是不要使用DI库。Go语言应该是一种简单易用的语言。DI库/框架会将其抽象化(在某种程度上使DI变得神奇)。

3wabscal

3wabscal3#

是的,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))

}
czfnxgou

czfnxgou4#

你也可以试试Dargo,这是一款新的软件,但是它有一些facebook one没有的特性,代码是here
下面是一个例子:
在本例中,一个名为SimpleService的服务将注入一个日志记录器。日志记录器本身是一个dargo服务,绑定了一个创建方法。该创建方法如下所示:

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

SimpleService的绑定将提供用于实现接口的结构体。该结构体有一个用inject注解的字段,后跟要注入的服务的名称。这是接口和用于实现它的结构体:

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!")
}

logger服务和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作用域中,这意味着每次注入或查找它时都将创建一个新的。
以下是使用查找服务的代码:

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等)。但是,服务不能有循环依赖关系。

qnakjoqk

qnakjoqk5#

Uber的Dig非常棒,下面是一篇关于它的博客文章:Dependency Injection in Go

r8uurelv

r8uurelv6#

如果你仍然对找到一个使用最少反射的Go语言DI库感兴趣,我做了一个叫axon的,它基于Google的Guice,所以如果你来自Java世界(像我一样),它应该很适合你期望它如何工作。
我在Web服务器中使用过它,它没有任何问题,速度足够快,对在CLI中使用没有任何影响。

amrnrhlw

amrnrhlw7#

你也可以查看Uber的fx来寻找一个更通用的应用框架,它使用了Uber的底层技术,@yndolok已经提到过。
fx github链接:https://github.com/uber-go/fx

gijlo24d

gijlo24d8#

作为作者,我可能有偏见,但https://github.com/muir/nject是Go语言最好的DI框架,它易于使用,并提供了非常完整的功能集。
它是基于类型的,给定一个函数列表,这些函数消耗和产生各种类型,它会把它们连接在一起,这样列表中的最后一个函数就会被调用,而其他任何需要为最后一个函数提供参数的函数也会被调用.

e0bqpujr

e0bqpujr9#

Go1.18增加了对泛型的支持,我认为依赖注入可以通过使用这种语言特性来实现,它现在已经内置在语言中,而不是外部依赖https://go.dev/blog/intro-generics

相关问题