首页 > 解决方案 > 避免将依赖项“向下传递”到实体子类

问题描述

我有以下问题:

我有一个名为 的超类Entity,它提供了每个实体都有的一些基本方法。为此,它需要一些服务才能运行,例如AbstractPotionService. 它的任何子类都不需要这种依赖关系,但是每次实例化实体的子类型时我都必须传递它,例如MobileEntity

public class Entity {
  
  private AbstractPotionService potionService;
  protected EntityTypeConverter converter;

  public Entity(EntityTypeConverter converter, AbstractPotionService potionService) {
    this.converter = converter;
    this.potionService = potionService;
  }

  public void addEffect(EffectType effectType, int durationInSeconds) {
    potionservice.applyEffectTo(this, effectType, durationInSeconds);
  }

  ...

}
public class MobileEntity extends Entity {

  public MobileEntity(EntityTypeConverter converter, AbstractPotionService potionService){
    super(converter, potionService);
  }

  public attack(MobileEntity victim) {
   ...
  }

  ...

}

这感觉有点不对劲,因为它是很多乏味的工作,并且当我想扩展Entity类时会使项目的可维护性降低。我的第一个想法是在这里使用单例并Entity直接(通过静态方法)在构造函数中获取服务的实例,但这不会创建上帝依赖吗?

我还阅读了关于作文的文章,但我找不到一个合适的例子来说明如何在我的案例中应用它。有人有想法吗?

标签: javainheritancedependency-injectionsingletoncomposite

解决方案


依赖注入原则、实践和模式(DIPP&P)(我合着)的第 4 章(第 106 页)指出:

除了通常的数据成员集之外,还包含行为的实体将很容易获得各种方法,每个方法都需要它们自己的依赖项。尽管您可能很想使用构造函数注入来注入此类依赖项,但这会导致需要创建每个此类实体及其所有依赖项的情况,即使对于给定的用例可能只需要少数几个。这使测试实体的逻辑变得复杂,因为所有依赖项都需要提供给构造函数,即使测试可能只对少数依赖项感兴趣。Method Injection [...] 提供了一个更好的选择。

在您的情况下,所需的构造函数依赖项的情况甚至被夸大了,因为每个子类型都需要在自己的构造函数中具有这些依赖项,并且需要将它们传递给Entity基类型。如果您向其添加新的依赖项,Entity将导致所有派生实体发生更改(以及它们的所有测试),这可能会导致可维护性问题。

因此,DIPP&P 建议改用方法注入,在您的情况下,它看起来像这样:

public class Entity {
  
  public Entity() { } // <-- No more Constructor Injection

  public void addEffect(
    EffectType effectType,
    int durationInSeconds,
    AbstractPotionService potionService) // <-- Method Injection
  {
    potionservice.applyEffectTo(this, effectType, durationInSeconds);
  }

  ...
}

使用方法注入,需要依赖项的方法随该依赖项一起提供。虽然该方法使用依赖项,但与构造函数注入不同,它不存储依赖项。

这意味着它现在是消费者提供正确依赖关系的责任。在大多数情况下,这意味着使用Constructor Injection将依赖项注入消费者。


推荐阅读