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

Java 现场注射-这是一个坏习惯吗?,java,dependency-injection,Java,Dependency Injection,我的同事和我的争论已经持续了几个星期了。我想听听社区对此的意见 我们在应用程序中使用guice。在这个应用程序的UI部分,我更喜欢通过DI和自定义注释(也称为工厂的PlaceIn)实例化所有字段和组件,并将它们连接到buildUI方法中 情况如下: 我的方法: @Inject @PreConfigured(caption="foo", width="100%") Label field; private void buildUI() { this.addComponent(field);

我的同事和我的争论已经持续了几个星期了。我想听听社区对此的意见

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

情况如下:

我的方法:

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

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

private void buildUI() {
   field = new Label();
   field.setCaption("foo");
   field.setWidth("100%");
   this.addComponent(field);
}
我还为数据绑定和i18n编写了一些注释

我的同事是这样做的:

他的方法:

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

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

private void buildUI() {
   field = new Label();
   field.setCaption("foo");
   field.setWidth("100%");
   this.addComponent(field);
}
这里的代码可能很长。考虑到数据绑定和其他主题,第二段代码可能会变得更长,而我的方法只是另一种注释

我的理由是:

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

  • 可读性
  • 视图代码并不是分散在所有地方
  • 该类在没有DI的情况下工作
  • 依赖注入的灵活性被高估了,特别是在注入具体类的情况下
  • 使用DI的唯一有效方法是构造函数注入
  • 但我也从Fowler那里读到了一些,他在书中说,setter注入(用场注入代替)是一个糟糕的设计决策。但是对于视图呢,它们在其他项目中没有任何重用

    现场注入是一种糟糕的设计吗?或者上面的另一种方式更优雅

    问候 基督教徒


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

    字段注入比单独调用setter更好。你为什么要花时间打电话给特定的设定者。依赖项注入的全部目的是避免在业务逻辑中为setter编写锅炉板代码

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

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

    我对你的理由表示:

  • 如果可能的话,我更喜欢在所有配置中使用java代码(因为它可以很容易地重构)。如果java代码太冗长,我更喜欢注释(这是您的情况),我会尽一切努力避免xml/yaml/任何其他标记语言配置
  • 实际上,您可以重用注释,从而将大量样板代码简化为简洁易读的注释。硬编码的值是不可重用的,但重用性在这里无论如何都不适用
  • 我没有对您的代码进行基准测试(因为我没有代码库),但是使用注释通常会涉及反射,这肯定比只调用setter要慢。所以我认为,如果这成为一个性能瓶颈,你会摸不着头脑如何处理涉及反射的部分
  • 当然。我的意思是把你的例子和他的比较一下。没有一个理智的人会说,阅读4行充满语法的代码比预先配置的(caption=“foo”,width=“100%”
  • 他的观点是:

  • 4中解释了它。
  • 他所说的“到处散落”是什么意思?在您的案例中,注释始终位于字段上,但如果以编程方式对其进行初始化,则代码可以位于任何位置。那是分散的
  • 如果您已经使用了Guice,那么不使用DI是没有意义的。他对DI有问题吗
  • 这就像“点名”。毫无意义。给他看保罗·格雷厄姆写的文章
  • 然后想象一个有20个场的物体。当然,拥有20个字段已经是一种代码味道,但任何超过4-5个参数的东西都是丑陋的。我个人更喜欢setter注入,因为它不涉及使用反射篡改
    private
    字段,也不涉及使用8-10个参数的构造函数。如果您想要不可变的对象,您可以使用
    Builder
    模式,如果您想要单例,您可以放心,Spring的(我认为Guice有相同的概念)单例范围适合您。顺便说一句,这是常识:如果这是唯一有效的方法,就没有其他方法了,对吗
  • 免责声明:由于这是一个主观的问题,我也会根据个人喜好在回答中提出一些主观的主张。这是我的两分钱:

    这两种方法在我看来都不好。

    你的不好是因为:

    • 您在一个模块中拥有所有绑定。想想这个模块能增长多大,特别是如果有很多屏幕的话。想一想这对可维护性的影响:现在一个想要改变一个屏幕的程序员必须

      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适合于大的事情(比如用解析器实现注入服务,注入控制)