使用google/wire进行依赖注入

Wire使用教程

Wire是Google提供的帮助Go开发人员实现编译时依赖注入的工具。
经过例子学习使用Wire。这里咱们要创建一个小的欢迎程序,用来了解如何使用Wire。git

构建欢迎程序的第一步

  • 让咱们建立一个小程序,它模拟一个事件,其中包含一个带有特定消息的欢迎来宾的问候语。
  • 咱们建立了三种数据类型github

    • 给迎宾员的信息
    • 传达信息的迎宾员
    • 以迎宾开始的活动
    type Message string
    
    type Greeter struct {
        // ... TBD
    }
    
    type Event struct {
        // ... TBD
    }
  • 如今,咱们将建立一个简单的初始化器,老是返回一个硬编码的消息:shell

    func NewMessage() Message {
        return Message("Hi there!")
    }
  • 咱们的迎宾员须要引用这条消息,咱们也为欢迎者建立一个初始化器。小程序

    func NewGreeter(m Message) Greeter {
        return Greeter{Message: m}
    }
    
    type Greeter struct {
        Message Message // <- adding a Message field
    }
  • 在初始化器中,咱们给Greeter分配了一个Message字段。如今,当咱们在Greeter上建立一个Greet方法时,咱们可使用这个消息:函数

    func (g Greeter) Greet() Message {
        return g.Message
    }
  • 接下来,Event须要有一个Greeter,因此咱们也会为它建立一个初始化器。工具

    func NewEvent(g Greeter) Event {
        return Event{Greeter: g}
    }
    
    type Event struct {
        Greeter Greeter // <- adding a Greeter field
    }
  • 而后咱们添加一个方法来启动事件:学习

    func (e Event) Start() {
        msg := e.Greeter.Greet()
        fmt.Println(msg)
    }

    Start方法是咱们这个小应用程序的核心:它告诉欢迎者发出一个问候语,而后将该消息打印到屏幕上。测试

  • 如今咱们已经准备好了应用程序的全部组件,让咱们看看在不使用Wire的状况下初始化全部组件须要作些什么。咱们的主函数是这样的:ui

    func main() {
        message := NewMessage()
        greeter := NewGreeter(message)
        event := NewEvent(greeter)
    
        event.Start()
    }

    首先咱们建立一个消息,而后用这个消息建立一个欢迎器,最后咱们用这个欢迎器建立一个事件。完成全部初始化以后,咱们就能够开始事件了。google

  • 咱们使用依赖注入设计原则。实际上,这意味着咱们传递每一个组件须要的任何内容。这种设计风格有助于编写易于测试的代码,并使一种依赖关系与另外一种依赖关系的交换变得容易。

使用Wire生成代码

依赖项注入的一个缺点是须要如此多的初始化步骤。让咱们看看如何使用Wire使初始化组件的过程更顺畅。

  • 咱们先简化咱们的主函数:

    func main() {
        e := InitializeEvent()
    
        e.Start()
    }
  • 接下来,在一个名为wire的单独文件中。咱们将定义InitializeEvent。这就是事情变得有趣的地方:

    // wire.go
    
    func InitializeEvent() Event {
        wire.Build(NewEvent, NewGreeter, NewMessage)
        return Event{}
    }

    与其依次初始化每一个组件并将其传递给下一个组件,不如使用一个链接调用。构建传递咱们想要使用的初始化器。在Wire中初始化器被称为“提供者”,即提供特定类型的函数。咱们为事件添加一个零值做为返回值,以知足编译器的要求。注意,即便咱们向事件添加值,Wire也会忽略它们。事实上,注入器的目的是提供关于使用哪些提供者来构造一个事件的信息,所以咱们将在文件顶部的build约束中把它从最终的二进制代码中排除掉:

    //+build wireinject
    
    # 相似
    //+build wireinject
    // The build tag makes sure the stub is not built in the final build.
    package main
  • InitializeEvent是一个“注入器”。如今咱们已经完成了注入器,可使用wire命令行工具了。

    # 安装工具
    go get github.com/google/wire/cmd/wire
  • 而后在与上述代码相同的目录中,简单地运行wire。Wire将找到InitializeEvent注入器并生成一个函数,其主体由全部必要的初始化步骤填充。生成文件为wire_gen.go

    // wire_gen.go
    
    func InitializeEvent() Event {
        message := NewMessage()
        greeter := NewGreeter(message)
        event := NewEvent(greeter)
        return event
    }

    它看起来就像咱们上面写的同样!如今,这是一个只有三个组件的简单示例,所以手工编写初始化器并不太困难。想象一下,对于复杂得多的组件,Wire是多么有用。当使用Wire时,咱们将提交两个Wire。去wire_gen。转到源代码控制。

使用Wire进行更改

  • 为了演示Wire如何处理更复杂的设置,让咱们重构Event的初始化器,以返回一个错误,而后看看会发生什么。

    func NewEvent(g Greeter) (Event, error) {
        if g.Grumpy {
            return Event{}, errors.New("could not create event: event greeter is grumpy")
        }
        return Event{Greeter: g}, nil
    }
  • 咱们会说有时候一个迎宾员可能脾气暴躁,因此咱们不能建立一个事件。NewGreeter的初始化如今看起来是这样的:

    func NewGreeter(m Message) Greeter {
        var grumpy bool
        if time.Now().Unix()%2 == 0 {
            grumpy = true
        }
        return Greeter{Message: m, Grumpy: grumpy}
    }

    咱们已经向Greeter结构中添加了一个grumpy的字段,若是初始化器的调用时间与Unix时代相比是偶数秒,那么咱们将建立一个暴躁的Greeter,而不是一个友好的Greeter。

    func (g Greeter) Greet() Message {
        if g.Grumpy {
            return Message("Go away!")
        }
        return g.Message
    }
  • 如今你明白了,一个脾气暴躁的迎宾员对一件事是多么地不利。所以NewEvent可能会失败。咱们的主如今必须考虑到InitializeEvent可能会失败:

    func main() {
        e, err := InitializeEvent()
        if err != nil {
            fmt.Printf("failed to create event: %s\n", err)
            os.Exit(2)
        }
        e.Start()
    }
  • 咱们还须要更新InitializeEvent,将错误类型添加到返回值:

    // wire.go
    
    func InitializeEvent() (Event, error) {
        wire.Build(NewEvent, NewGreeter, NewMessage)
        return Event{}, nil
    }
  • 再次运行wire生成代码,以下

    // wire_gen.go
    
    func InitializeEvent(phrase string) (Event, error) {
        message := NewMessage(phrase)
        greeter := NewGreeter(message)
        event, err := NewEvent(greeter)
        if err != nil {
            return Event{}, err
        }
        return event, nil
    }

    Wire检查注入器的参数,看到咱们向参数列表中添加了一个字符串(例如,phrase),一样看到在全部提供程序中,NewMessage接受一个字符串,所以它将phrase传递给NewMessage。

完整的代码

相关文章
相关标签/搜索