我必须创建一个具有本地和外部存储的订单系统。应始终首先使用本地存储,如果仍有任何缺失,则应由外部存储器订购。
基本上我有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。然而,我并不完全清楚如何轻松地选择正确的方法。在我的案例中,最佳做法是什么?
如果能够更好地处理正确策略的选择,我可以接受建议。谢谢你的时间!
答案 0 :(得分:2)
您的ReservationHandler目前负责至少做三件事:创建Reservator实例,计算要使用的Reservator,以及充当ReservationContext。虽然这段代码并不是太糟糕,但是我们可以很容易地让Guice处理创建Reservator实例 - 毕竟它的工作是封装依赖项创建 - 并考虑提取创建正确的储存器。如果Reserveators的数量增加,或者如果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
;如果列表很长或可能会改变,那么注入提供者会很有意义。如果您的实例难以在测试中使用,那么注入Providers也是有意义的,因为您可以使用Providers.of()
和mock来用简单的确定性测试double替换真正的LocalReservator(等等)
如果你想在此基础上构建,你还可以参与Guice multibindings(它允许你以分布式方式在Guice中创建一个Set或Map,一次一个绑定)或Assisted Injection(可以让你在创建预备库时传递构造函数参数,使用你定义的工厂而不是Guice的提供者。两者都不是立即适用的,但两者都值得学习。
从这里开始,如果你的setAmount
逻辑足够复杂,你也可以将它提取到一个方法类ReservatorFactory,它选择要返回的Reservator。这样,您的ReservationHandler可以自行承担责任,并且可以在不执行特定预留逻辑的情况下进行测试。 (当然,如果您希望ReservationHandler做的唯一事情,那么请务必将该实现留在原处。否则您可以将ReservationHandler重构为空shell!)
P.S。杰克在评论中是正确的,将@Singleton
标记为@Singleton
同时保留个人预订状态可能非常危险。您可以删除api.get('some server url').then(function(data) {
if (data.SomeDate) data.SomeDate = new Date(data.SomeDate);
});
注释,Guice每次都会创建一个新实例;这是Guice的默认行为。