现场注射 - 这是一个坏习惯吗?

时间:2013-09-18 09:55:53

标签: java dependency-injection

我和我的同事现在已经有几个星期有争执了。我想听听社区对此的意见。

我们在申请中使用guice。在这个应用程序的UI部分,我更喜欢通过DI和自定义注释(也称为工厂的位置)实例化所有字段和组件,并在buildUI方法中将它们连接起来。

如下所示:

我的方法:

@Inject
@PreConfigured(caption="foo", width="100%")
Label field;

private void buildUI() {
   this.addComponent(field);
}

我还写了一些数据绑定注释和i18n。

我的同事这样做:

他的方法:

private Label field;

private void buildUI() {
   field = new Label();
   field.setCaption("foo");
   field.setWidth("100%");
   this.addComponent(field);
}

这里的代码可能很长。考虑到数据绑定和其他主题,第二个代码可以变得更长,而我的方法只是另一个注释。

我的理由是:

  1. 配置所有组件的行为和外观的中心位置
  2. 代码重用
  3. 性能 - 在长时间运行的应用程序中,组件的初始化代码变得“热”,jit会对其进行更多优化。
  4. 更具可读性。
  5. 他的理由是:

    1. 可读性
    2. 视图代码并非遍布整个地方。
    3. 课程没有DI
    4. 依赖注入的灵活性被高估,尤其是在注入具体类时。
    5. 使用DI的唯一有效方法是构造函数注入。
    6. 但是我还从福勒那里读到了一些articles,他指出,定位注射(用场注入替代)是一个糟糕的设计决定。但那些在其他项目中没有重用的视图呢?

      Field Injection是一个糟糕的设计吗?或者上面介绍的另一种方式更优雅?

      亲切的问候 基督教

      PS:我知道讨论的开始只是基于意见,但这是一个长期持续的讨论(setter vs constructor injection),这可能是值得放手的。

3 个答案:

答案 0 :(得分:3)

免责声明:由于这是一个主观问题,我还会根据个人偏好在这个答案中提出一些主观主张。这是我的两分钱:

这两种方法对我来说都很糟糕。

你的不好是因为:

  • 您拥有模块中的所有绑定。想想这个模块有多大,特别是如果有很多屏幕的话。想想这个可持续性的含义:现在想要改变单个屏幕的程序员必须

    1- Know the DI framework
    2- Find the module where the bindings for this screen are defined.
    3- Scroll down to find the binding.
    4- Make the change in the module, maybe also in the screen.
    5- Commit changes for the module class, maybe also for the screen class.
    
  • 使用DI意味着使用反射,这比不使用反射慢。

  • 使您的代码依赖于第三方代码(DI框架)
  • 部分布局仍然在屏幕上进行了硬编码(容器视图是什么以及它们包含什么),而部分是在另一个模块中。单一责任原则被打破了。
  • 注释是"脏":现在源文件是用两种不同的语言编写的。它的可读性较差,使用自动代码分析工具时也会出现问题。
  • 难以编写单元测试(顺便说一下,测试应该使用DI框架来运行,这是一件坏事,特别是如果有很多测试的话,会慢一点。)

你的同事的方法当然是有缺陷的,因为它包含硬依赖性。不过,这对屏幕来说可能是可以接受的。我认为DI可以用于大事(比如使用解析器实现注入服务,注入控制器,daos,记录器),但我不会将它用于视图,而且我宁愿坚持使用构造函数注入。所以(在Android中)我只是在xml中定义视图并在Activity类中编写视图控制器部分,这就是你应该这样做的方式。

答案 1 :(得分:1)

我认为您的问题并不适合StackOverflow(请查看常见问题解答),但我可以向您介绍我的想法。

我支持你在这个问题上的想法,因为你似乎已经在这个项目中使用了DI,为什么你不使用它提供的功能呢?

我对你的理由的评论:

  1. 如果可能的话,我更喜欢使用java代码进行所有配置(因为它可以很容易地重构)。如果java代码太冗长,我更喜欢注释(这是你的情况),我会做任何事情来避免 xml / yaml /任何其他标记语言配置。
  2. 您实际上可以重复使用注释,这样可以将许多样板代码简化为简洁和可读的注释。硬编码值不可重复使用,但无论如何都不适用于此处的可重用性
  3. 我没有对您的代码进行基准测试(因为我没有代码库),但使用注释通常涉及反射,这肯定比调用setter慢。所以我认为,如果这成为一个性能瓶颈,你会抓住你的头脑如何处理涉及反射的部分
  4. 当然。我的意思是将你的例子与他的比较。没有理智的人会说,阅读充满语法的4行代码比@PreConfigured(caption="foo", width="100%")
  5. 更具可读性

    他的观点:

    1. 4.
    2. 中解释
    3. “散落在各地”是什么意思?注释总是在您的案例中的字段上,但如果您以编程方式初始化它,则代码可以在任何位置。那是分散的。
    4. 如果您已经使用过Guice,那么不使用DI是没有意义的。他有DI问题吗?
    5. 这就像“骂人”。没有意义。向他展示保罗格雷厄姆撰写的this文章。
    6. 然后想想一个有20个字段的对象。当然,有20个字段已经是代码气味,但任何超过4-5个参数的东西都是丑陋的。我个人更喜欢setter注入,因为它不涉及用反射篡改private字段,也不涉及具有8-10个参数的构造函数。如果你想要不可变对象你可以使用Builder模式,如果你想要单身,你可以放心,Spring(我认为Guice有相同的概念)单身范围对你来说很好。顺便说一句,这是常识:如果它是唯一有效的方式就没有别的办法了,对吗?

答案 2 :(得分:0)

现场注入比单独调用setter更好。你为什么要花时间打电话给特定的制定者。依赖注入的重点是避免业务逻辑中的setter的样板代码。