共计 2779 个字符,预计需要花费 7 分钟才能阅读完成。
什么是依赖注入(DI)
要了解什么是依赖注入首先咱们要晓得什么事管制反转
管制反转
管制反转是面向对象编程中的一种设计准则或者设计模式,能够用来升高代码之间的耦合度,例如如下代码,class A 中有一个属性的类型是 class B,如果在不应用管制反转的模式时,class 须要本人新建 class B 的实例,这就导致了耦合。为理解耦,就呈现了管制反转的模式,行将依赖的取得反转,不再由应用方创立依赖,而是由内部零碎注入。
class A {Entity B;}
什么是依赖注入
依赖注入是一种规范的技术,用于通过灵便地向组件提供其工作所需的所有依赖关系来生成灵便且松耦合的代码
依赖注入是管制反转的一种实现形式,其余的实现形式还有依赖查找,他们之间的区别
- 依赖注入是在对象创立时将对象的依赖通过某种形式注入
- 依赖查找是指在须要时,调用对象能够通过的框架所提供的办法取得须要的依赖。
依赖注入的实现形式
依赖注入的形式次要有 3 种:
- 结构器注入
- setter 注入
- 接口注入
目前业界把 依赖查找
的一些实现形式也当做 依赖注入
,例如 服务定位器
,所以他们之间的界线并不是很清晰
wire 简介
wire 是一种依赖注入工具,它采纳的是结构器注入的形式,然而在是线上它并不是通过反射来实现的。在这篇 blog 中,go 团队阐明了为什么不采纳反射的形式实现。次要的起因是思考到反射的实现会把谬误带到运行时并且是难以了解和调试的,而 go 团队更违心在编译时就发现此问题。
实例
在示例代码中咱们会有一个 speaker
和一个 message
,在 speaker 初始化时须要一个message
,而后调用speaker
的Say
办法打印出 message
的文字
无依赖注入
能够看到,在此版本中,speaker
须要本人结构 message
,很显著,这就是一个耦合点,speaker
的实现耦合了 message
的实现,接下来,让咱们革新代码,将它变成结构器注入的模式。
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()}
结构器注入
能够看到,这个版本跟无依赖注入的版本的差距只是 message 的实例不再是由 speaker
初始化,而是交由内部注入,这样的益处是咱们能够有多个 NewMessage
的实现,具体注入的是哪个实现与 speaker
的实现解耦了。
这项技术在小规模时成果很好,然而较大的应用程序可能具备简单的依赖关系图,从而导致一大堆初始化代码依赖于程序,个别很难将代码清晰地合成,特地是因为某些依赖项被屡次应用。将服务的一种实现替换为另一种实现可能会很苦楚,因为这波及通过增加一组新的依赖关系,并删除未应用的旧依赖关系来批改依赖关系图。
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 版本
wire 旨在简化初始化代码的治理。能够通过代码或配置来形容服务及其依赖关系,而后 Wire 解决生成的图形以弄清楚程序以及如何将每个服务传递给它所须要的货色。通过更改函数签名或增加或删除初始化程序来更改应用程序的依存关系,而后让 Wire 做繁琐的工作来为整个依存关系图生成初始化代码。
让咱们首先增加一个wire.go
留神顶部的构建束缚,是用来告知 go 编译时不须要包含此文件
//+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{}}
而后让咱们革新一下main.go
能够看到,speaker
不再是应用 NewSpeaker
函数创立,而是应用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()}
那么,InitializeSpeaker
的实现是这么样的呢?咱们能够在我的项目下执行 wire
命令,wire
会主动给咱们生成 wire_gen.go
文件, 能够看到,咱们不须要本人编写实现,wire 会自动识别依赖关系并为咱们生成代码
// 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
}
最初
在此篇文章中并未提到 wire 的一些高级个性和概念,例如 injector、provider、接口绑定等,有趣味的同学能够查看 wire 的文档理解