如何在重构庞大的课程时使用spring

时间:2012-07-11 19:30:03

标签: java spring class-design

这是一个关于java类设计以及如何与spring交互的一般性问题,但我会尝试使用一个例子来使它更易于讨论。 (请注意,此代码主要是psedo代码,已排除了大量的漏洞)。

想象一下,我们遇到的是一个庞大而且考虑不周的阶级。它做了很多事情,使得重用,测试或理解变得更加困难。在我的示例中,我们有一个“BulkyThingDoer”类,它既可以执行某项操作,也可以跟踪有关完成任务的次数以及需要多长时间(用于发布到JMX)的统计信息。并不是很糟糕,但随着统计数量的增加,班级变得由簿记主导,而实际重要的事情就是在噪音中迷失了。如果我们有一个竞争的DoerInterface实现,我们需要重现相同的统计工具等。

@Component
@ManagedResource
public class BulkyThingDoer implements DoerInterface, DoerStatistics{
  private int thingsDone = 0;
  private long timeSpentDoingThings = 0;

  public void doThing(){
    long start = System.currentTimeMillis();
    // do the important thing
    thingsDone ++;
    timeSpentDoingThings += System.currentTimeMillis() - start;
  }
  public int getThingsDone(){ return thingsDone;}
  public long getTimeSpentDoingThings (){ return timeSpentDoingThings;}
}

现在让我们想象一下,我们重构这个类,将所有统计簿记拆分成一个辅助对象,然后让实际的doer报告(尽可能最高级别)发生的事情。现在,ThingDoer和DoerStatistics对象都是简单,可测试的单责任对象。

@Component
public class ThingDoer implements DoerInterface {
  private final DoerStatistics stats;

  public ThingDoer(DoerStatistics stats){
    this.stats = stats;
  }

  public void doThing(){
    long start = System.currentTimeMillis() - start;
    // do the important thing
    stats.recordThatWeDidThing(System.currentTimeMillis() - start);
  }
}

@ManagedResource
public class DoerStatisticHolder implements DoerStatistics{
  private int thingsDone = 0;
  private long timeSpentDoingThings = 0;

  public void recordThatWeDidThing(long duration){
    thingsDone ++;
    timeSpentDoingThings += duration;
  }
  public int getThingsDone(){ return thingsDone;}
  public long getTimeSpentDoingThings (){ return timeSpentDoingThings;}
}

对我来说很明显,这将是一个很好的重构,我认为这是将DI原则一直延伸到详细的实现。

这是这个问题的全部内容:一旦我完成了这个重构,我的类在弹簧上下文文件中变得更难使用。之前我可以使用单个定义并且相同的对象将被创建为bean我可以给(或自动装配)到其他对象我现在必须在我的上下文中定义多个部分并手动将它们连接在一起。这为用户提供了比必要更多的实现细节。

另一种方法是有效地重建“BulkyThingDoer”辅助类,用正确的部分构造我的对象:

public class DelegatedBulkyThingDoer extends DoerInterface{
  ThingDoer doer;

  public DelegatedBulkyThingDoer(){
    DoerStatistics stats = new DoerStatisticHolder();
    doer= new ThingDoer(stats );
  }

  public void doThing(){
    doer.doThing();
  }
}

这解决了一些可用性问题,但现在我没有在DoerStatisticHolder或ThingDoer上获得任何弹簧魔法。在这个例子中,这意味着JMX从未注册过。如果有一种简单的方法可以告知Spring这些创建的子对象可能会解决我的问题。

我当然可以委托我们在原始Bulky实现中调用的所有调用,并删除两个实现类中的所有注释,但这对我来说听起来非常不优雅。我实际上会再次重建BulyClass,这尤其令人烦恼,因为它只是像JMX或日志记录这样的跨界问题所必需的。

我认为这种痛苦导致最初的实施变得如此庞大和复杂。由于这样的困难,Spring似乎促使开发人员停止在实现级别应用严格的OOP原则。在弹跳的代码库中,我已经深入研究了这种模式,这些类最终会在单个类中实现,以支持“高级”弹簧上下文使用(单个bean def),而不是因为我们因此而崩溃发生这种情况时会失去春天的魔力。

我是spring和java的新手(过去几年一直是c ++,scala和ruby),所以我可能会缺少一些东西。 java / spring开发人员如何处理弹簧可用性和良好分解实现之间的“阻抗不匹配”?他们咬紧牙关并创建DelegatingBulkyClass,强制用户构建我的重构impl类或者只是坚持使用BulkyClass模式,因为它最简单?

1 个答案:

答案 0 :(得分:0)

与BulkyThingDoer一样,您可以使用@Component注释DoerStatisticHolder,因此它将由spring管理,然后您可以使用@Autowire将其自动装入ThingDoer。一个缺点是你还需要一个非参数构造函数。

如果您始终需要对象的新实例,则可以使用@Scope("prototype")对其进行额外注释。

使用基于注释的配置方法,仅使用一个实际实现来自动装配类,可以节省大量的xml配置开销。