繁体   English   中英

如何使用依赖注入来处理策略

[英]How to handle a strategy with dependency injection

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

基本上我有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);
  }

}

现在,我想知道如何通过依赖项注入而不是实例化来简化测试,从而处理三种保留策略(LocalReservator,ExternalReservator和MixedReservator)。

我知道我可以绑定Reservator-Interace的所有实现并在注入时获取一个List。 但是,对我来说,目前尚不清楚我如何轻松地选择正确的。 我的最佳做法是什么?

另外,如果可以更好地选择正确的策略,我也欢迎提出建议。 谢谢你的时间!

您的ReservationHandler当前负责至少做三件事:创建Reservator实例,计算要使用的Reservator,并充当ReservationContext。 尽管这段代码并不是很糟糕,但我们可以轻松地让Guice处理创建Reservator实例-毕竟,这就是封装依赖项创建的工作,并考虑提取出正确的Reservator的创建。 如果Reservators的数量增加,或者ReservationContext增长以具有更清晰的范围,这也将使您绝缘。 (现在,它看起来就像一个包装器接口。)

您要查找的不是一组列表,而是一组提供程序:

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

您可以通过从Guice的构造函数中接收它们(可以为您在图中绑定的任何T自动注入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 ; 如果列表很长或可能会更改,则注入提供程序很有意义。 如果您的实例很难在测试中使用,则注入Provider也很有意义,因为您可以使用Providers.of()和一个模拟来用一个简单的确定性测试double替换实际的LocalReservator(其余的依次类推) 。

如果您希望以此为基础,还可以涉及Guice多重绑定 (可让您以分布式方式在Guice中创建Set或Map,一次绑定一个)或Assisted Injection (可让您在Reservator中传递构造函数参数)创建,使用您定义的工厂而不是Guice的提供者)。 两者都不是立即适用的,但是两者都值得学习。

从这里开始,如果setAmount逻辑足够复杂以至于无法提取,则还可以将其提取到一个方法类ReservatorFactory中,该类选择要返回的Reservator。 这样,您的ReservationHandler可以自己承担责任,无需进行特定的保留逻辑即可进行测试。 (当然,如果这是您希望ReservationHandler唯一要做的事情,那么请务必将该实现保留在原处。否则,您可以将ReservationHandler重构为一个空shell!)

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM