Guice随笔 C#CC++SpringEclipse
程序员文章站
2022-07-14 14:44:36
...
随着时间的推移. 当初吵翻了天的依赖注入再也不是什么新鲜玩意儿.
在日复一日给资本家拉磨的平淡中, Spring和自己写的小玩具也都被扔在记忆的垃圾堆里不必提起. 对Guice的激情也逐渐磨灭到麻木和冷淡了.
日常一些邮件列表中, 仍然能看到对使用Guice之类框架的反感或者死忠的两种截然不同的观点碰撞, 甚至于是否使用依赖注入也仍然没有尘埃落定.
个人从前是依赖注入的坚定粉丝, 也是Guice的欢呼者. 到现在我在需要使用一个框架的时候还是会使用Guice, 但是, 各位看官, 如果你们以前曾经见过那个挥舞着Guice超级大棒见什么都砸几下的不屈身影, 他已经移民去火星了.
如今, 我理想的使用Guice的方法. 就是说我有一堆类, 互相有依赖关系, 假设A依赖B, C, D; B依赖X, Y, Z; C依赖于D, B和D需要是Singleton, 那么就写成:
如果你对Guice熟悉的话, 就会看出来这正是Guice幼儿园小班级别的代码啊.
这就对了, 几年用下来的经验告诉我, Guice对这种最简单的情况的处理方式是最简单, 最直接的. 不需要写什么Module, 不需要什么binding, 那些东西带来太多的复杂性.
很多对依赖注入或者Guice的疑虑主要集中在代码不容易理解, 依赖关系被隐藏在Module中不容易发现等等. 但是我发现这些疑虑在尽量有意减少module和binding的前提下, 都不再是问题.
比如我在读A类的代码, 需要知道B到底是怎么回事, 直接一个F3(Eclipse), 就到了B的代码里面. 然后需要知道X是怎么回事, 再一个F3. 许多在线代码检查工具也支持这种简单的导航.
相比之下, 手工注入(就是说, 某个地方你需要手工调用new A(b, c, d))反而难于理解, 因为我还要去找new A()在什么地方调用的, B, C, D都是怎么传递进来的.
当然, 这个前提是我的B, C, D, X, Y, Z都是类而不是interface. 我发现在一个大的代码库里, interface很多时候是有害的. 因为它们人为的增加了一个间接层, 增大了跟踪导航阅读代码的难度. 在加上在Guice里你就需要额外的Module, 额外的binding, 额外的@ForApple, @ForOrange的注解, 然后Module多了, 你就要面对如何管理整个项目, 怎么样在不同的main()函数里选择不同的Module, 而又避免不能把一个Module通过不同的父Module安装两遍等等复杂讨厌的问题.
interface只有在真正存在那么一个自然的抽象的情况下才是适合的. 比如, 一个List和ArrayList就是两个不同的抽象. java.sql.Connection和任何特定的Connection实现也是两个不同的抽象.
而如果你需要写一个User, Authenticator, 或者TaskMonitor, 直接写就是了. 不管三七二十一上来先一个IUser接口加一个User实现, 或者一个User接口加一个UserImpl实现, 那就是人为增加的复杂性. 类和接口命名的尴尬雷同就已经表明这根本不是一个自然的抽象.
okay, 理想如此, 事实上当然不可能永远都没有一个接口, 两个实现的情况出现. 不过, 如果Module和binding只在有这种需求的时候才写的话, Guice代码也还可以相对容易地管理和理解. (当然, 还要小心robot leg问题的出现, 然后如果你用assistedinject来绕过这个问题的话, 又是一些额外的复杂性需要管理和维护了).
嗯, 嗯. 大致就随笔到这里. 回头我还会写写为什么我不喜欢assistedinject, 以及为什么Guice的Module会难以管理等等.
在日复一日给资本家拉磨的平淡中, Spring和自己写的小玩具也都被扔在记忆的垃圾堆里不必提起. 对Guice的激情也逐渐磨灭到麻木和冷淡了.
日常一些邮件列表中, 仍然能看到对使用Guice之类框架的反感或者死忠的两种截然不同的观点碰撞, 甚至于是否使用依赖注入也仍然没有尘埃落定.
个人从前是依赖注入的坚定粉丝, 也是Guice的欢呼者. 到现在我在需要使用一个框架的时候还是会使用Guice, 但是, 各位看官, 如果你们以前曾经见过那个挥舞着Guice超级大棒见什么都砸几下的不屈身影, 他已经移民去火星了.
如今, 我理想的使用Guice的方法. 就是说我有一堆类, 互相有依赖关系, 假设A依赖B, C, D; B依赖X, Y, Z; C依赖于D, B和D需要是Singleton, 那么就写成:
class A { @Inject A(B b, C c, D d) {...} } @Singleton class B { @Inject B(X x, Y y, Z z) {...} } class C { @Inject C(D d) {...} } @Singleton class D { @Inject D(...) {} }
如果你对Guice熟悉的话, 就会看出来这正是Guice幼儿园小班级别的代码啊.
这就对了, 几年用下来的经验告诉我, Guice对这种最简单的情况的处理方式是最简单, 最直接的. 不需要写什么Module, 不需要什么binding, 那些东西带来太多的复杂性.
很多对依赖注入或者Guice的疑虑主要集中在代码不容易理解, 依赖关系被隐藏在Module中不容易发现等等. 但是我发现这些疑虑在尽量有意减少module和binding的前提下, 都不再是问题.
比如我在读A类的代码, 需要知道B到底是怎么回事, 直接一个F3(Eclipse), 就到了B的代码里面. 然后需要知道X是怎么回事, 再一个F3. 许多在线代码检查工具也支持这种简单的导航.
相比之下, 手工注入(就是说, 某个地方你需要手工调用new A(b, c, d))反而难于理解, 因为我还要去找new A()在什么地方调用的, B, C, D都是怎么传递进来的.
当然, 这个前提是我的B, C, D, X, Y, Z都是类而不是interface. 我发现在一个大的代码库里, interface很多时候是有害的. 因为它们人为的增加了一个间接层, 增大了跟踪导航阅读代码的难度. 在加上在Guice里你就需要额外的Module, 额外的binding, 额外的@ForApple, @ForOrange的注解, 然后Module多了, 你就要面对如何管理整个项目, 怎么样在不同的main()函数里选择不同的Module, 而又避免不能把一个Module通过不同的父Module安装两遍等等复杂讨厌的问题.
interface只有在真正存在那么一个自然的抽象的情况下才是适合的. 比如, 一个List和ArrayList就是两个不同的抽象. java.sql.Connection和任何特定的Connection实现也是两个不同的抽象.
而如果你需要写一个User, Authenticator, 或者TaskMonitor, 直接写就是了. 不管三七二十一上来先一个IUser接口加一个User实现, 或者一个User接口加一个UserImpl实现, 那就是人为增加的复杂性. 类和接口命名的尴尬雷同就已经表明这根本不是一个自然的抽象.
okay, 理想如此, 事实上当然不可能永远都没有一个接口, 两个实现的情况出现. 不过, 如果Module和binding只在有这种需求的时候才写的话, Guice代码也还可以相对容易地管理和理解. (当然, 还要小心robot leg问题的出现, 然后如果你用assistedinject来绕过这个问题的话, 又是一些额外的复杂性需要管理和维护了).
嗯, 嗯. 大致就随笔到这里. 回头我还会写写为什么我不喜欢assistedinject, 以及为什么Guice的Module会难以管理等等.
上一篇: Hive搭建
下一篇: 地图着色——core.logic求解