鉴于此代码:
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)
}
以主方式完成的组件接线是否正确地将依赖关系连接在一起?好像我在代码中使用了这一点。有没有比这更好的共同模式,还是我在思考它?
答案 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