通过对web应用框架背后原理的探索,引入了依赖注入的概念。如果你需要读懂或者写一个框架的话,依赖注入的思想绝对能帮到你。本文记录对依赖注入的探索。
区分依赖和宿主
现在我们把问题简化一下:
package main
import "fmt"
func MethodA(name string, f funca intb )) {
fmt.Println("Enter MethodA:"name)
f(3030"zdd") // 给f注入参数
"Exit MethodA:")
}
MethodB) abmain() d := MethodB
"zddhub"d}
在上述的例子中,为了完成MethodA的功能,需要依赖MethodB方法的实现,把MethodB方法叫做MethodA的依赖(Dependency)
,MethodA叫做宿主(Host)
。上面的例子直接在Host中调用f(3030,"zdd")
来给依赖注入参数。或者换一个角度理解,将自定义的方法MethodB整体注入到MethodA的执行环境中
。为了通用性考虑,通常会实现一个依赖注入器。
依赖注入器
接下来实现一个依赖注入器(Injector)。在web应用框架的例子中,我们发现,在Host中,只能拿到依赖方法的参数类型,而我们需要将参数类型和参数值建立联系,因此,在Injector中封装一个map[Type]Value
是自然而然的选择。
稍微改进一下我们的注入器,让它更通用些。
依赖注入的另一种使用场景
依赖注入的另一种使用方法是用在依赖属性的构造上,通常在测试框架中应用广泛。通过下面的例子,理解一下吧。
Camera // 定义一个单反相机
Name string
L Lens
Lens {
LensType int // 0: 普通镜头,1: 广角镜头,2: 长焦镜头
c CameraCapture// 使用不同的镜头拍照
m []3m[],152)">12= "normal lens""wide-angle lens""telephoto lens"
value cL))
index int
Invalid index Interface().(LensLensType
} else = 0
"capture with"index])
// 持有一个全局的注入器,复用之前实现的注入器。
()
c {"ZddCamera"{}} // 组装一个普通镜头的单反相机,通常组装一个单反需要很长的时间,假设你有工匠情结,并且DIY成功的概率是100%,努力工作一个月就可以组装完一台。
() // 拍照
// 如果想继续测试广角镜头和长焦镜头的拍照效果,那么是不是要这样?
// d := Camera{"ZddCamera",Lens{1}} // 你又努力工作了一个月
// e := Camera{"ZddCamera",Lens{2}} // 你又努力工作了一个月
// 如果Boss告诉你,最近又进口了十几款镜头,你是不是感觉要死的心都有了呢。
// 正常人只需要组装新的镜头
wLens } // 努力工作十天
tLens // 努力工作十天
wLens// 复用之前的单反,只更换镜头
// 拍照
tLens// 拍照
// 是不是感觉棒棒嗒
//: capture with normal lens
//: capture with wide-angle lens
//: capture with normal lens
}
测试框架猜测
比如Android的测试框架通常会出现形如@Inject
的注释,它都做了些什么事情呢?下面给出一个完整可运行的例子程序来说明一下:
string
// @Inject // <--你在这里说明了L是需要注入的,通常用注释来说明注入
L "telephoto lens"
//* fmt.Println("capture with",m[c.L.LensType]) // 这是你在程序中的代码,框架看到你使用了c.L
// 默默的给你做了如下替换:
getLensLensType}
// 由框架在编译时插入的方法
Lens l Lens
l }
}
l
}}
// 你需要用某种方式进行注入
()
}
好了,以上解释了依赖注入的使用和原理,如果你要读懂或者写一个框架的话,反射和依赖注入都是很有用的特征,那就掌握它吧。或者用的时候来读读这篇文章吧。
上述注入器的bug
你一定发现了上述注入器的bug:
你会发现单用类型做拓扑的注入器,只会持有最新一次类型的值。那么怎么办呢?没有解决不了的问题,传个[]string
也是极好的。如果参数的结构比较固定的话,封装成struct也是不错的选择,Go允许给struct打Tag,而reflect也支持对Tag的访问,如果对这部分感兴趣,推荐你去读一下"encoding/xml"
,"encoding/json"
包的源码。
什么? 还有bug!!!,留言或者发邮件告诉我吧,欢迎各种骚扰。另外,赞赏作者里有微信号哦,一般人我不告诉他。
理解依赖注入了吗?现在读odegangsta/inject是不是感觉很溜呀?facebook还开源了一套基于有向图实现的注入器facebookgo/inject感觉屌屌的。如果对Java情有独钟,那就研究一下Dagger吧,相信你一定会有所收获。
依赖注入的另一种使用场景
依赖注入的另一种使用方法是用在依赖属性的构造上,通常在测试框架中应用广泛。通过下面的例子,理解一下吧。
Camera // 定义一个单反相机
Name string
L Lens
Lens {
LensType int // 0: 普通镜头,1: 广角镜头,2: 长焦镜头
c CameraCapture// 使用不同的镜头拍照
m []3m[],152)">12= "normal lens""wide-angle lens""telephoto lens"
value cL))
index int
Invalid index Interface().(LensLensType
} else = 0
"capture with"index])
// 持有一个全局的注入器,复用之前实现的注入器。
()
c {"ZddCamera"{}} // 组装一个普通镜头的单反相机,通常组装一个单反需要很长的时间,假设你有工匠情结,并且DIY成功的概率是100%,努力工作一个月就可以组装完一台。
() // 拍照
// 如果想继续测试广角镜头和长焦镜头的拍照效果,那么是不是要这样?
// d := Camera{"ZddCamera",Lens{1}} // 你又努力工作了一个月
// e := Camera{"ZddCamera",Lens{2}} // 你又努力工作了一个月
// 如果Boss告诉你,最近又进口了十几款镜头,你是不是感觉要死的心都有了呢。
// 正常人只需要组装新的镜头
wLens } // 努力工作十天
tLens // 努力工作十天
wLens// 复用之前的单反,只更换镜头
// 拍照
tLens// 拍照
// 是不是感觉棒棒嗒
//: capture with normal lens
//: capture with wide-angle lens
//: capture with normal lens
}
测试框架猜测
比如Android的测试框架通常会出现形如@Inject
的注释,它都做了些什么事情呢?下面给出一个完整可运行的例子程序来说明一下:
好了,以上解释了依赖注入的使用和原理,如果你要读懂或者写一个框架的话,反射和依赖注入都是很有用的特征,那就掌握它吧。或者用的时候来读读这篇文章吧。
上述注入器的bug
你一定发现了上述注入器的bug:
你会发现单用类型做拓扑的注入器,只会持有最新一次类型的值。那么怎么办呢?没有解决不了的问题,传个[]string
也是极好的。如果参数的结构比较固定的话,封装成struct也是不错的选择,Go允许给struct打Tag,而reflect也支持对Tag的访问,如果对这部分感兴趣,推荐你去读一下"encoding/xml"
,"encoding/json"
包的源码。
什么? 还有bug!!!,留言或者发邮件告诉我吧,欢迎各种骚扰。另外,赞赏作者里有微信号哦,一般人我不告诉他。
理解依赖注入了吗?现在读odegangsta/inject是不是感觉很溜呀?facebook还开源了一套基于有向图实现的注入器facebookgo/inject感觉屌屌的。如果对Java情有独钟,那就研究一下Dagger吧,相信你一定会有所收获。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。