背景
在开发过程中或者写单测过程中,经常遇到这种情况,为了初始化类A而需要初始化类A中所以依赖的类。
例如,类A中有一个属性是类B,则在初始化类A的时候,需要初始化一个类B的实例,这种操作导致类A和类B耦合太紧,不方便解耦,尤其是测试的类A的时候不方便。当类A中依赖了多个类,并且需要初始化多次类A的时候,不方便的地方更明显。
为了解决这个问题,大家想到一种方法,在初始化类A的时候,可以通过注入的方式来初始化类B,而且尽量简洁、优雅、开发者少感知,这种方法就是依赖注入。
名词解释
- DI:Dependency Injection,依赖注入,是一种设计模式。其作用是去除类之间的依赖关系,实现松耦合,以便于开发测试
fx
Fx是uber开业的一个golang版本的依赖注入框架,它使得golang通过可重用、可组合的模块化来构建golang应用程序变得非常容易。
示例代码
package main
import (
"context"
"fmt"
"github.com/uber-go/fx"
)
func main() {
type t3 struct {
Name string
t4 t4
}
type t4 struct {
Age int
}
var (
v1 *t3
v2 *t4
)
app := fx.New(
fx.Provide(func() *t3 { return &t3{"hello everybody!!!"} }),
fx.Provide(func() *t4 { return &t4{2019} }),
fx.Populate(&v1),
fx.Populate(&v2),
)
app.Start(context.Background())
defer app.Stop(context.Background())
fmt.Printf("the reulst is %v , %v\n", v1.t4.Name, v2.Age)
}
通过将构造函数传入到fx中,然后调用Populate完成变量之间依赖关系的映射。
dig
dig 是 uber 开源的依赖注入库。
示例代码
package main
import (
"fmt"
"github.com/jessevdk/go-flags"
"go.uber.org/dig"
"gopkg.in/ini.v1"
)
type Option struct {
ConfigFile string `short:"c" long:"config" description:"Name of config file."`
}
func InitOption() (*Option, error) {
var opt Option
_, err := flags.Parse(&opt)
return &opt, err
}
func InitConf(opt *Option) (*ini.File, error) {
cfg, err := ini.Load(opt.ConfigFile)
return cfg, err
}
func PrintInfo(cfg *ini.File) {
fmt.Println("App Name:", cfg.Section("").Key("app_name").String())
fmt.Println("Log Level:", cfg.Section("").Key("log_level").String())
}
func main() {
container := dig.New()
container.Provide(InitOption)
container.Provide(InitConf)
container.Invoke(PrintInfo)
}
使用dig的一般流程:
- 创建一个容器:
dig.New; - 为想要让
dig容器管理的类型创建构造函数,构造函数可以返回多个值,这些值都会被容器管理; - 使用这些类型的时候直接编写一个函数,将这些类型作为参数,然后使用
container.Invoke执行我们编写的函数。
这是通过函数传参的形式实现依赖注入,它也可以自动初始化结构体的属性。在结构体中加入dig.In之后,结构体中的类属性就会自动调用初始化函数来初始化。
命名依赖实现同一个类有多个实例
type MongodbClient struct {}
func NewMongoClient(cfg *Mongo.Client) *MongodbClient{
return ConnectionMongo(cfg)
}
type MongodbClients struct {
dig.Out
Mgo1 *MongodbClient `name:"mgo1"`
Mgo2 *MongodbClient `name:"mgo2"`
}
container.Provide(NewConfig, dig.Name("mgo1"))
container.Provide(NewMongoClient, dig.Name("mgo1"))
container.Provide(NewMongoClient, dig.Name("mgo2"))
m MongodbClients
wire
google的一个开源依赖注入框架
使用wire前
func CreateConcatService() *ConcatService {
logger := &Logger{}
client := NewHttpClient(logger)
return NewConcatService(logger, client)
}
使用wire后
func CreateConcatService() *ConcatService {
panic(wire.Build(
wire.Struct(new(Logger), "*"),
NewHttpClient,
NewConcatService,
))
}
$ go get github.com/google/wire/cmd/wire
$ wire
生成wire_gen.go文件
func CreateConcatService() *ConcatService {
logger := &Logger{}
httpClient := NewHttpClient(logger)
concatService := NewConcatService(logger, httpClient)
return concatService
}
inject
Facebook 开源的依赖注入框架
示例代码
package main
import (
"fmt"
"os"
"github.com/facebookgo/inject"
)
type Animal struct {
Cat *Cat `inject:""`
Dog *Dog `inject:""`
}
func (a *Animal) Detail() string {
return fmt.Sprintf("Cat:%s\nDog:%s", a.Cat.Detail(), a.Dog.Detail())
}
type Cat struct {
Name string
Age int
Roommate *Dog `inject:"object_name"`
}
func (c *Cat) Detail() string {
return fmt.Sprintf("i am cat. my name is %s, my age is %d, i have a roommate:%s", c.Name, c.Age, c.Roommate.GetName())
}
type Dog struct {
Name string
Age int
}
func (d *Dog) Detail() string {
return fmt.Sprintf("i am dog. my name is %s, my age is %d", d.Name, d.Age)
}
func (d *Dog) GetName() string {
return d.Name
}
func main() {
var g inject.Graph
var a Animal
err := g.Provide(
&inject.Object{Value: &a},
//&inject.Object{Value: &Cat{Name: "cat1", Age: 8}},
&inject.Object{Value: &Dog{Name: "dog1", Age: 9},Name:"xx"},
)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
if err := g.Populate(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
fmt.Println(a.Detail())
}
// output:
Cat:i am cat. my name is cat1, my age is 8, i have a roommate:dog1
Dog:i am dog. my name is dog1, my age is 9
inject 轻量级DI框架,代码不足700行。它提前将有依赖关系的对象的实例提供好,然后调用Populate设置对象之间的依赖关系。
比较
参考
[2]www.jianshu.com/p/847e1c8d2…
[3]studygolang.com/articles/18…
[4]zhuanlan.zhihu.com/p/67032669
[7]www.cyhone.com/articles/fa…
[9]zhuanlan.zhihu.com/p/108518676
[11]stackoverflow.com/questions/4…