java - 如何处理依赖注入(inject)策略

标签 java dependency-injection guice strategy-pattern

我必须创建一个具有本地和外部存储的订单系统。本地存储应始终先用完,如果还有缺少的内容,则应由外部存储订购剩余的内容。

基本上我有4种可能的情况:

1 从本地存储订购所有商品
2 混合从本地存储和外部存储订购商品
3 从外部存储订购所有项目
4 无法订购

现在我决定为 3 个有效案例制作一个界面,界面非常简单:

public interface Reservator{
  boolean reserveItem(Article article, amount);
}

现在,为了选择哪种策略,我使用以下界面:

public interface ReservationContext{
  boolean setAmount(int amount);
  boolean reserveItem(Article article);
}

其实现大致如下:

@Singleton
public ReservationHandler implements ReservationContext{
  private int reservationAmount=0;
  private Reservator reservator;
  private LocalStorage local;
  private ExternalStorage external;

  @Inject
  public ReservationHandler(LocalStorage local, ExternalStorage external){
    this.local = local;
    this.external = external;

  }
  @Override
  public boolean setAmount(int amount){
    if(amount == 0){
      return false;
    }
    if(local.getStorage > amount){
      reservator = new LocalReservator(//all dependencies);
    }
    else if(local.getStorage ==0 && external.getStorage > amount){
      reservator = new ExternalReservator(//all dependencies);
    }
    else if((local.getStorage + external.getStorage) > amount){
      reservator = new MixedReservator(//all dependencies);
    }
    else{
      return false;
    }
    reservationAmount = amount;
    return true;
  }

  @Override
  public boolean reserveItem(Article article){
    return reservator.reserveItem(article, amount);
  }

}

现在我想知道如何通过依赖注入(inject)来处理三种预留策略(LocalReservator、ExternalReservator 和 MixedReservator),而不是实例化以允许更轻松的测试。

我知道我可以绑定(bind) Reservator-Interace 的所有实现并在注入(inject)时获取一个列表。然而,我并不完全清楚如何轻松选择正确的一个。针对我的情况,最佳实践是什么?

此外,如果可以更好地选择正确的策略,我愿意接受建议。感谢您的宝贵时间!

最佳答案

您的 ReservationHandler 当前至少负责做三件事:创建 Reservator 实例、计算要使用的 Reservator 以及充当 ReservationContext。虽然这段代码并不算太糟糕,但我们可以轻松地让 Guice 处理创建 Reservator 实例(毕竟,这是它的工作,封装依赖项创建),并考虑提取正确的 Reservator 的创建。如果 Reservator 的数量增加,或者 ReservationContext 的范围变得更清晰,这也将使您隔离。 (现在它看起来只是一个包装接口(interface)。)

您正在寻找的是一组提供程序,而不是列表:

private final Provider<LocalReservator> localReservatorProvider;
private final Provider<ExternalReservator> externalReservatorProvider;
private final Provider<MixedReservator> mixedReservatorProvider;

您可以通过在构造函数中从 Guice 接收它们来设置它们(Guice 可以为您在图表中绑定(bind)的任何 T 自动注入(inject) Provider<T>),然后您可以在您的方法中调用它们。

if(local.getStorage > amount){
  reservator = localReservatorProvider.get();  // no dependencies!
}
else if(local.getStorage ==0 && external.getStorage > amount){
  reservator = externalReservatorProvider.get();
}
else if((local.getStorage + external.getStorage) > amount){
  reservator = mixedReservatorProvider.get();
}
else{
  return false;
}

您应该这样做吗?现在您还没有向我们展示您的 Reservator 实例的依赖关系,或者它们是否可能会更改。如果不是,和/或列表很短,您可能会坚持使用 new ;如果列表很长或者可能会发生变化,那么注入(inject)提供者就很有意义。如果您的实例在测试中难以使用,那么注入(inject)提供程序也是有意义的,因为您可以使用 Providers.of() 使用模拟来用简单的确定性测试替身替换真正的 LocalReservator(其余的依此类推)。

如果您想在此基础上进行构建,您还可以涉及 Guice multibindings (它允许您以分布式方式在 Guice 中创建一个 Set 或 Map,一次一个绑定(bind))或 Assisted Injection (这将允许您在创建 Reservator 期间传递构造函数参数,使用您定义的工厂而不是 Guice 的 Provider)。两者都不是立即适用的,但两者都值得学习。

从这里开始,如果您的setAmount逻辑足够复杂,无法提取,您也可以将其提取到单方法类 ReservatorFactory 中,该类 ReservatorFactory 选择要返回的 Reservator。这样,您的 ReservationHandler 就可以由其自己负责,并且可以在不执行特定预订逻辑的情况下进行测试。 (当然,如果这是您希望 ReservationHandler 做的唯一事情,那么请务必将该实现保留在原处。否则您可以将 ReservationHandler 重构为空 shell!)

附: jack 在评论中是正确的,将其标记为 @Singleton 可能非常危险。同时它保留个人预订的状态。您可以删除 @Singleton注解和Guice每次都会创建一个新实例;这是 Guice 的默认行为。

关于java - 如何处理依赖注入(inject)策略,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43678738/

相关文章:

java - 优化排序矩阵中第 N 个最大元素的代码

javascript - webpack:如何从 "bower_components"获取 JavaScript,而不是从 "node_modules"获取 JavaScript

java - 使用 Guice 的通用类型提供程序

java - 使用 Dagger 2.0 仅在两个 Activity 之间共享对象

scala - 如何使用 Play 2.4 将服务注入(inject)到 Actor 中?

java - Guice 使用注释将两个不同的类绑定(bind)到同一个接口(interface)

java - 是否可以覆盖对注入(inject)器的调用?

java - 错误的 xml 编码

java - 无法识别的 VM 选项 'UseCompressedStrings' - Intellij IDEA 无法启动

java - Hadoop 构建在 Windows 7、Maven 3.1.1、Jdk 1.7.0_45 上失败(Hadoop src 2.2.0)