依赖注入是软件工程中经常使用到的一种技术,它提供了一种控制反转的机制,把控制权利交给了调用方。调用方来决定使用哪些参数,哪些对象来进行具体的业务逻辑。
依赖注入的本质其实是为了将组件的创建与其依赖的创建分离
实现原理:
- 通过反射读取对象的依赖(golang是通过tag实现)
- 在容器中查找有无该对象实例
- 如果有该对象实例或者创建对象的工厂方法,则注入对象或使用工厂创建对象并注入
- 如果无该对象实例,则报错
好处:
1 它让调用方更灵活。
2 大量减少定义类型的代码量
3 增加代码的可用性,因为调用方只需要关注它需要的参数,不需要顾及它不需要的参数了。
正常我们创建一个对象,首先需要构建对象的构造函数,调用时将入参加入进去,然后其中一旦有一个入参需要更改,所有函数的入参都需要修改:
type Cat struct {
color String
}
func (cat *Cat) Start() {
pass
}
func NewCat() *Cat {
return &Cat{
color : "read"
}
}
type Person struct {
cat *Cat
}
func (service *Person) Start() []*Person {
service.cat.Start()
}
func NewPerson(cat *Cat) *Person{
return &Person{cat: cat}
}
func main() {
cat := NewCat()
person := NewPerson(cat)
person.Satrt()
}
而如果使用以来注入的方式:
type Cat struct {
color String
}
func (cat *Cat) Start() {
pass
}
func NewCat() *Cat {
return &Cat{
color : "read"
}
}
type Person struct {
cat *Cat
}
func (service *Person) Start() []*Person {
service.cat.Start()
}
func NewPerson(cat *Cat) *Person{
return &Person{cat: cat}
}
func BuildContainer() *dig.Container {
container := dig.New()
container.Provide(NewCat)
container.Provide(NewPerson)
return container
}
func main() {
container := BuildContainer()
err := container.Invoke(func(person *Person) {
person.Start()
})
if err != nil {
panic(err)
}
}
这个虽然看起来没有简化多少,但是如果依赖的比较多的时候,就需要初始化一堆对象,然后调用对应的函数,而依赖注入:
- 容器认识到我们要求的是构建
persion
- 它确定函数 NewPersion提供该类型
- 接下来它确定 NewPersion函数依赖
Cat
- 它找到了
Cat
的提供者,也就是 NewCat
-
NewCat
没有任何依赖关系,所以它被调用
-
NewCat
的结果是一个 Cat
传递给 NewPersion
- NewPersion的结果是
*persion
被传递给 Invoke
这个就是依赖注入的本质,