Home  >  Article  >  Backend Development  >  What is go language dependency injection?

What is go language dependency injection?

青灯夜游
青灯夜游Original
2023-01-18 16:41:241795browse

In the Go language, dependency injection (DI) is a design pattern that decouples dependencies between components; when needed, different components can obtain information from other components through a unified interface. objects and states. The benefit of dependency injection is decoupling; and decoupling can bring more benefits: enhanced code scalability, enhanced code maintainability, easier unit testing, etc.

What is go language dependency injection?

The operating environment of this tutorial: Windows 7 system, GO version 1.18, Dell G3 computer.

What is dependency injection?

When I first heard this word, I was confused and it was hard to pronounce. Maybe many students who have studied spring think this is very basic and easy to understand knowledge, but Because I have never learned Java or spring before, I was very confused when I first came into contact with this term.

Dependency injection, the English name is dependency injection, or DI for short. The word dependency is easy to understand. In software design, there are large and small dependencies from architectural modules to function methods.

For example, new B needs to be created before new A. A depends on B. At this time, we can say that B is a dependency of A. A controls B. There is a coupling relationship between AB, and the code design The idea is that it is best to achieve loose coupling. If B needs to be transformed one day, then A will also need to be transformed. You may be fine with this being a dependency, but if there is a series of dependencies between A->B->C->D->E->F, then it will be very troublesome to transform.

At this time, something is needed to decouple the strong coupling between them. How to decouple it? We can only use third-party forces. We put A to B This idea of ​​handing over control to a third party is called Inversion of Control (IOC Inversion Of Control), and this third party is called an IOC container. What the IOC container has to do is to create a new B, and then inject this instance of B into A. Then A can normally use the method based on B. This process is called dependency injection, and based on IOC This method is called dependency injection.

Simply put, dependency injection (DI) is a design pattern that decouples dependencies between components. When needed, different components can obtain objects and states in other components through a unified interface. The interface design of Go language avoids many situations where third-party dependency injection frameworks need to be used (such as Java, etc.). Our injection solution only provides very few injection solutions similar to those in Dager or Guice, and focuses on avoiding manual configuration of dependencies between objects and components.

The benefits of dependency injection

If you understand the idea of ​​dependency injection, you should also understand the biggest benefit it brings - decoupling .

And decoupling can bring more benefits: enhanced code scalability, enhanced code maintainability, easier unit testing, etc.

So how to implement dependency injection?

There are the following methods in Java:

  • Setter method injection: implement the public set method of specific properties to allow the external container to call the object of the dependent type .

  • Interface-based injection: Implement a specific interface for external containers to inject objects of dependent types.

  • Constructor-based injection: implement a constructor with specific parameters, and pass in the object of the dependent type when creating a new object.

  • Annotation-based injection: Add specific keywords to the code to achieve injection.

Annotations are the most common way. Like comments, they are not executed as code, but are specifically for others to read. But the readers of annotations are entirely humans, and the main readers of annotations, in addition to humans, are frameworks or precompilers.

Go dependency injection-wire

wire is an annotation-based dependency injection method. wire is a dependency injection tool open sourced by Google. We only need to tell wire the dependencies between types in a special go file, and it will automatically Help us generate code, help us create objects of specified types, and assemble its dependencies.

wire has two basic concepts, Provider (constructor) and Injector (injector).

Let wire know how to generate these dependent objects by providing the provider function. wire According to the injector function signature we defined, the complete injector function is generated. The injector function is the final function we need. It will Call provider in dependency order. The requirements of

wire are very simple. Create a new wire.go file (the file name can be arbitrary) and create our initialization function. For example, if we want to create and initialize a Mission object, we can do this:

//+build wireinject

package main

import "github.com/google/wire"

func InitMission(name string) Mission {
  wire.Build(NewMonster, NewPlayer, NewMission)
  return Mission{}
}

You can see the annotation on the first line: build wireinject, indicating that this is an injector. build is actually a feature of the Go language. Similar to C/C conditional compilation, some options can be passed in when executing go build, and based on these options it is decided whether certain files are compiled. The wire tool will only process files with wireinject, so our wire.go file must add this.

In the function, we call wire.Build() to pass in the constructor of the type that Mission depends on. For example, you need to call NewMission() to create the Mission type. NewMission() accepts two parameters, one of Monster type and one of Player type. Monster type objects need to be created by calling NewMonster(), and Player type objects need to be created by calling NewPlayer(). So NewMonster() and NewPlayer() we also need to pass to wire.

After writing the wire.go file and executing the wire command, a wire_gen.go file will be automatically generated.

// Code generated by Wire. DO NOT EDIT.

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

package main

// Injectors from wire.go:

func InitMission(name string) Mission {
  player := NewPlayer(name)
  monster := NewMonster()
  mission := NewMission(player, monster)
  return mission
}

You can see that wire automatically generates the InitMission method for us. In this method, player, monster and mission are initialized in sequence. Then we only need to call this InitMission in our main function.

func main() {
  mission := InitMission("dj")

  mission.Start()
}

Before dependency injection was used, our code looked like this:

func main() {
  monster := NewMonster()
  player := NewPlayer("dj")
  mission := NewMission(player, monster)

  mission.Start()
}

Isn’t it much simpler? There are only three object initializations here. If there were more, you might not realize the benefits of dependency injection.

For example:

wire.go文件:
// +build wireinject
// The build tag makes sure the stub is not built in the final build.

package di

import (
	"github.com/google/wire"
)

//go:generate kratos t wire
func InitApp() (*App, func(), error) {
	panic(wire.Build(dao.Provider, service.Provider, http.New, grpc.New, NewApp))
}

实现文件:
//dao
var Provider = wire.NewSet(New, NewDB, NewRedis)
//service
var Provider = wire.NewSet(New, wire.Bind(new(pb.Server), new(*Service)))


生成的wire_gen.go 文件:
func InitApp() (*App, func(), error) {
	redis, cleanup, err := dao.NewRedis()
	if err != nil {
		return nil, nil, err
	}
	db, cleanup2, err := dao.NewDB()
	if err != nil {
		cleanup()
		return nil, nil, err
	}
	daoDao, cleanup3, err := dao.New(redis, db)
	if err != nil {
		cleanup2()
		cleanup()
		return nil, nil, err
	}
	serviceService, cleanup4, err := service.New(daoDao)
	if err != nil {
		cleanup3()
		cleanup2()
		cleanup()
		return nil, nil, err
	}
	engine, err := http.New(serviceService)
	if err != nil {
		cleanup4()
		cleanup3()
		cleanup2()
		cleanup()
		return nil, nil, err
	}
	server, err := grpc.New(serviceService)
	if err != nil {
		cleanup4()
		cleanup3()
		cleanup2()
		cleanup()
		return nil, nil, err
	}
	app, cleanup5, err := NewApp(serviceService, engine, server)
	if err != nil {
		cleanup4()
		cleanup3()
		cleanup2()
		cleanup()
		return nil, nil, err
	}
	return app, func() {
		cleanup5()
		cleanup4()
		cleanup3()
		cleanup2()
		cleanup()
	}, nil
}

So, what exactly is dependency injection?

It’s just encapsulation and decoupling.

【Related recommendations: Go video tutorial, Programming teaching

The above is the detailed content of What is go language dependency injection?. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn