使用Golang已经好几年了,开发了好多项目,有大有小。当然大的也大不到哪去,毕竟没有一个项目做得长久。之前从Java转到Go,好多思维也跟着过来,其中一个就是IoC。如果在Java没使用过IoC,基本上可以说没使用过Java。
IoC的好处街知巷闻,但在Go的世界里,IoC可有可无,至少我使用的第三方库中,几乎没看到IoC的影子。
不用New?
Java使用IoC的其中一个好处是:如果要更换一个接口实现了,依赖此接口的对象的初始化由IoC解决,不用你去手动修改代码。这里的代码主要是构造函数的参数,或者SetXXX的参数。
背后的魔法是大量的发射以及可能的配置(XML,annotation)。程序启动到初始化完成不会很快。所以Java程序往往给人一直很慢的错觉。
Go较弱的annotation和反射功能,无法达到Java那种IoC的高度。但Go绝大多数情况编译出来是一个可执行的文件。编译器从main.go出发,逐个遍历依赖的代码,最后都打包到可执行程序中。没有被import的代码,最终编译后的文件是不包含这段代码的,这个时候反什么射啊。
Go程序来说,无法避免手动new。即使使用了IoC库,用法也方便不到哪去。但是New其实不繁琐,编译器会通知你需要修改参数。如果你的对象是singleton,其实往往只需要改一处地方。带来的好处是启动速度快得惊人。
Constructor or Property
Java的Spring支持对象的构造函数参数注入和Property(SetXXX)注入。在xml时代,property有个优势:书写顺序随意,显而易懂。到了Annotation时代,就只有劣势了。
我个人比较喜欢构造函数方式,构造函数天然就有定义对象依赖的语义。不用看具体实现,看函数声明就知道这个对象在依赖什么。同时发现问题更快速:参数数量和类型对应不上,在启动阶段就抛异常。
Property就很困扰,因为并不是所有的SetXXX都是required,有些甚至是接口的功能,不是依赖。同时少注入依赖又不会在启动阶段发现问题,直到运行到调用该依赖的方法时才抛出空指针异常,有时带来相当大的灾难。这是多么崩溃的事情,annoation在一定程度上减少这种灾难的频率,前提是你要标注上。
在Go中,我都是NewXXX函数,归编译器的事情就应该推给编译器。只可惜Go没有optional类型,interface可以传nil啊,写if xx == nil是多么累和丑。