

go-tip
在讲解Kratos的过程中,我们引入了google推出的wire这个工具。我们先阅读一下官方的定义:
Wire is a code generation tool that automates connecting components using dependency injection.
从关键词入手:
我们从具体的case着手,学习wire这个工具。
我简化了官方的示例,给出一个注释后的代码,方便大家阅读:
package main
// Part-1 Message对象
type Message string
func NewMessage() Message {
return Message("Hi there!")
}
// Part-2 Greeter对象,依赖Message
func NewGreeter(m Message) Greeter {
return Greeter{Message: m}
}
type Greeter struct {
Message Message
}
func (g Greeter) Greet() Message {
return g.Message
}
func main() {
message := NewMessage()
greeter := NewGreeter(message)
greeter.Greet()
}
这里的调用很直观,分为3步:
NewMessage创建Message对象NewGreeter方法,将Message对象注入到Greeter对象里Greeter的方法,其实内部用到了前面注入的Message对象依赖注入的详细定义可以参考链接 - https://en.wikipedia.org/wiki/Dependency_injection,我就不赘述了。这里我用具体的case进行对比,方便大家理解:
type Greeter struct {
Message Message
}
// 依赖注入
func NewGreeter(m Message) Greeter {
return Greeter{Message: m}
}
func (g Greeter) Greet() Message {
return g.Message
}
// 非依赖注入
func NewGreeter() Greeter {
return Greeter{}
}
func (g Greeter) Greet() Message {
g.Message = NewMessage()
return g.Message
}
看完例子,可能大家对DI已经有个初步的概念了,我这边再重复一下关键点:
Greeter的方法Greet()会依赖内部的Message对象,所以我们说 - Greeter的实现依赖MessageMessage的初始化分为两种:创建Greeter对象前和调用Greet方法时,前者被称为依赖注入,相当于在初始化时把依赖项注入进去,而不是使用时再创建。我们先安装wire工具:
go get github.com/google/wire/cmd/wire
再编写一个wire.go
//+build wireinject
package main
import "github.com/google/wire"
func InitializeGreeter() Greeter {
wire.Build(NewGreeter, NewMessage)
return Greeter{}
}
运行命令wire gen生成wire_gen.go
// Code generated by Wire. DO NOT EDIT.
//go:generate go run github.com/google/wire/cmd/wire
//+build !wireinject
package main
// Injectors from wire.go:
func InitializeGreeter() Greeter {
message := NewMessage()
greeter := NewGreeter(message)
return greeter
}
最后,可以在main函数里使用
func main() {
greeter := InitializeGreeter()
greeter.Greet()
}
可以看到,wire这个工具基本能力就体现在wire.Build(NewGreeter, NewMessage)里,把这里面的两个初始化函数串联了起来,形成了一个整体的InitializeGreeter。
我们新增一个方法,初始化结果里增加一个error返回值:
// Part-3 Greeter对象,依赖Message,并且返回error方法
func NewGreeterV2(m Message) (Greeter, error) {
if m == "" {
return Greeter{}, errors.New("empty message")
}
return Greeter{Message: m}, nil
}
然后在wire.go里调整函数返回值增加一个error
func InitializeGreeter() (Greeter, error) {
wire.Build(NewGreeterV2, NewMessage)
return Greeter{}, nil
}
最后,在wire_gen.go里生成了带error的新方法
func InitializeGreeterV2() (Greeter, error) {
message := NewMessage()
greeter, err := NewGreeterV2(message)
if err != nil {
return Greeter{}, err
}
return greeter, nil
}
我们新增一个方法,增加一个name的入参
// Part-3 Greeter对象,依赖Message和参数name,并且返回error方法
func NewGreeterV3(m Message, name string) (Greeter, error) {
if name == "" {
return Greeter{}, errors.New("empty name")
}
return Greeter{Message: m}, nil
}
wire.go里也增加一个string类型的入参(变量名可以任意)
func InitializeGreeterV3(greetName string) (Greeter, error) {
wire.Build(NewGreeterV3, NewMessage)
return Greeter{}, nil
}
最后生成对应的方法
func InitializeGreeterV3(greetName string) (Greeter, error) {
message := NewMessage()
greeter, err := NewGreeterV3(message, greetName)
if err != nil {
return Greeter{}, err
}
return greeter, nil
}
Wire里面提了两个关键性的概念,为了方便大家阅读文档时能快速理解,我这里再专门说明下:
NewXXXGithub - https://github.com/google/wire
DI - https://en.wikipedia.org/wiki/Dependency_injection
wire工具的实现逻辑很清晰 - 按一定规则组装多个Provider到Injector中。
生成的代码 结构简单而具有规律,所以用代码生成技术很有价值,既减少了重复性工作,又能引入DI的思想方便程序的扩展。
至此,我们对wire的基础用法已经了然于胸,但更多的价值需要深入理解DI这个概念,最好能结合到具体的工程实践上。如果你对这块还没有太深刻的理解,建议结合网上的相关资料了解DI在工程中的价值,会让你使用wire这个工具时更有感触。
Github: https://github.com/Junedayday/code_reading Blog: http://junes.tech/ Bilibili: https://space.bilibili.com/293775192