Go依赖注入组件wire最佳实践(一)
什么是依赖注入?
贴一段维基百科中关于依赖注入的解释:
在软件工程中,依赖注入(dependency injection,缩写为 DI)是一种软件设计模式,也是实现控制反转的其中一种技术。这种模式能让一个对象接收它所依赖的其他对象。“依赖”是指接收方所需的对象。“注入”是指将“依赖”传递给接收方的过程。在“注入”之后,接收方才会调用该“依赖”[1]。此模式确保了任何想要使用给定服务的对象不需要知道如何建立这些服务。取而代之的是,连接收方对象(像是 client)也不知道它存在的外部代码(注入器)提供接收方所需的服务。
一个依赖注入的最简单示例
package tutorial
import "fmt"
type Message string
func NewMessage() Message {
return Message("Hello Message")
}
type Greeter struct {
message Message
}
func NewGreeter(msg Message) Greeter {
return Greeter{message: msg}
}
func (g Greeter) Greet() Message {
return g.message
}
type Event struct {
greeter Greeter
}
func NewEvent(greeter Greeter) Event {
return Event{greeter: greeter}
}
func (e Event) Start() {
msg := e.greeter.Greet()
fmt.Println(msg)
}
定义了几个类型和函数来模拟一个简单的问候事件。接下来看下如何使用Event
来创建一个事件
package tutorial
import "testing"
func TestEvent_Start(t *testing.T) {
msg := NewMessage()
greeter := NewGreeter(msg)
event := NewEvent(greeter)
event.Start()
}
从上面的代码看出如果要New一个Event
对象出来,我们先New一个Greeter
,在NewGreeter
的时候它依赖了Message对象
,New一个Message。
我们正在使用依赖注入的设计原则。在实践中,这意味着我们传递每个组件所需的内容。这种设计风格适合编写易于测试的代码,并且很容易用另一个依赖项替换。
使用wire来生成代码
依赖注入的一个缺点是需要很多初始化步骤。让我们看看如何使用Wire来使我们组件的初始化过程更加顺畅。
安装wire
go install github.com/google/wire/cmd/wire@latest
编写wire注入函数
// wire.go
//go:build wireinject
// +build wireinject
package tutorial
import "github.com/google/wire"
func InitializeEvent() Event {
wire.Build(NewEvent, NewGreeter, NewMessage)
return Event{}
}
相对于逐个初始化每个组件并将其传递到下一个组件中,我们可以通过单个调用wire.Build来传入我们想要使用的初始化器。
在Wire中,初始化器被称为“提供者”,即提供特定类型的函数。我们添加了一个Event的零值作为返回值,以满足编译器。
请注意,即使我们向Event添加值,Wire也会忽略它们。实际上,注入器的目的是提供有关使用哪些提供者来构建事件的信息,因此我们将在文件顶部使用构建约束将其从最终二进制文件中排除。
//+build wireinject
生成wire_gen.go代码
在wire.go所在的目录,执行命令:
wire .
# 控制台会告诉你生成结果以及目录,类似下面的输出:
# wire: github.com/NanmiCoder/PracticeGoprojects/wirecase/tutorial: wrote /Users/nanmi/workspace/myself_code/PracticeGoprojects/wirecase/tutorial/wire_gen.go
查看生成的wire_gen.go代码,是不是放下跟上面我们自己构造的一样呢?
// wire_gen.go
// Code generated by Wire. DO NOT EDIT.
//go:generate go run github.com/google/wire/cmd/wire
//go:build !wireinject
// +build !wireinject
package tutorial
// Injectors from wire.go:
func InitializeEvent() Event {
message := NewMessage()
greeter := NewGreeter(message)
event := NewEvent(greeter)
return event
}
现在这是一个简单的例子,只有三个组件,所以手动编写初始化器并不太痛苦。想象一下在一些大型的项目中,组件会很多很多,使用wire组件用处就提现出来了 。