当前位置:网站首页>Getting started with go wire dependency injection

Getting started with go wire dependency injection

2020-11-07 20:55:00 Leoython

What is dependency injection (DI)

To understand what dependency injection is, we first need to know what inversion of control is

Inversion of control

Inversion of control is a design principle or design pattern in object-oriented programming , Can be used to reduce the coupling between code , For example, the following code , class A The type of an attribute in is class B, If you don't use inversion of control mode ,class You need to create your own class B Example , This leads to coupling . To decouple , There's a pattern of inversion of control , About to reverse the acquisition of dependence , The dependency is no longer created by the user , It's injected by an external system .

class A {
    Entity B;
}

What is dependency injection

Dependency injection is a standard technology , Used to generate flexible and loosely coupled code by flexibly providing components with all the dependencies they need to work

Dependency injection is an implementation of inversion of control , Other implementations are dependency lookup , The difference between them

  1. Dependency injection is to inject the dependency of an object in some way when the object is created
  2. Dependency lookup is when needed , The calling object can obtain the required dependency through the method provided by the framework of .

Implementation of dependency injection

The main ways of dependency injection are 3 Kind of :

  1. Constructor Injection
  2. setter Inject
  3. Interface injection

At present, the industry puts Dependency lookup Some of the ways to implement are as follows Dependency injection , for example Service locator , So the boundaries between them are not very clear

wire brief introduction

wire Is a dependency injection tool , It takes the form of constructor Injection , But on the line, it's not done by reflection . In this article blog in ,go The team explained why it wasn't implemented in a reflective way . The main reason is that the implementation of reflection will bring errors to the runtime and is difficult to understand and debug , and go The team would prefer to find this problem at compile time .

example

In the example code, we will have a speaker And a message, stay speaker Initialization requires a message, And then call speaker Of Say Method to print out message Words of

No dependency injection

You can see , In this release ,speaker You need to build it yourself message, Obviously , This is a coupling point ,speaker The implementation of is coupled with message The implementation of the , Next , Let's transform the code , Turn it into a pattern of constructor Injection .

package main

import "fmt"

type Message string

func NewMessage(text string) Message {
    return Message(text)
}

type Speaker struct {
    Message Message
}

func (s Speaker) Say() {
    fmt.Println(s.Message)
}

func NewSpeaker(text string) Speaker {
    m := NewMessage(text)
    return Speaker{
        Message: m,
    }
}

func main() {
    s := NewSpeaker("hello, I am a speaker")
    s.Say()
}

Constructor Injection

You can see , The difference between this version and the version without dependency injection is just message An example of is no longer by speaker initialization , It's about being injected from the outside , The advantage of this is that we can have multiple NewMessage The implementation of the , Which implementation and speaker The implementation of is decoupled .

This technology works well on a small scale , But larger applications can have complex dependency diagrams , As a result, a lot of initialization code depends on the order , It is generally difficult to decompose code clearly , Especially because some dependencies are used many times . Replacing one implementation of a service with another can be painful , Because it involves adding a new set of dependencies , And delete the unused old dependencies to modify the dependency graph .

package main

import "fmt"

type Message string

func NewMessage(text string) Message {
    return Message(text)
}

type Speaker struct {
    Message Message
}

func (s Speaker) Say() {
    fmt.Println(s.Message)
}

func NewSpeaker(m Message) Speaker {
    return Speaker{
        Message: m,
    }
}

func main() {
    m := NewMessage("hello, I am a speaker")
    s := NewSpeaker(m)
    s.Say()
}

wire edition

wire Designed to simplify the management of initialization code . Services and their dependencies can be described by code or configuration , then Wire Process the generated graph to figure out the order and how to deliver each service to what it needs . Change application dependencies by changing the function signature or adding or removing initializers , And then let Wire Do the tedious work of generating initialization code for the entire dependency graph .

Let's first add a wire.go

Note the construction constraints at the top , It's used to tell go You don't need to include this file at compile time

//+build wireinject
// The build tag makes sure the stub is not built in the final build.

package main

import "github.com/google/wire"

func InitializeSpeaker(text string) Speaker {
    wire.Build(NewSpeaker, NewMessage)
    return Speaker{}
}

And then let's transform main.go

You can see ,speaker No longer in use NewSpeaker Function creation , But use InitializeSpeaker

package main

import "fmt"

type Message string

func NewMessage(text string) Message {
    return Message(text)
}

type Speaker struct {
    Message Message
}

func (s Speaker) Say() {
    fmt.Println(s.Message)
}

func NewSpeaker(m Message) Speaker {
    return Speaker{
        Message: m,
    }
}

func main() {
    s := InitializeEvent("hello, I am a speaker")
    s.Say()
}

that ,InitializeSpeaker This is how it works ? We can execute under the project wire command ,wire It will be automatically generated for us wire_gen.go file , You can see , We don't need to write our own implementations ,wire Will automatically identify dependencies and generate code for us

// Code generated by Wire. DO NOT EDIT.

//go:generate wire
//+build !wireinject

package main

// Injectors from wire.go:

func InitializeSpeaker(text string) Speaker {
    message := NewMessage(text)
    speaker := NewSpeaker(message)
    return speaker
}

Last

There is no mention of wire Some of the advanced features and concepts of , for example injector、provider、 Interface binding, etc , Interested students can check wire Of file understand

版权声明
本文为[Leoython]所创,转载请带上原文链接,感谢