3

这是一个关于 java 类设计以及它如何与 spring 交互的一般性问题,但我将尝试使用一个示例使其更容易讨论。(请注意,此代码主要是伪代码,已排除大量绒毛)。

想象一下,我们遇到了一个很大且分解很差的类。它正在做很多事情,这使得重用、测试或理解变得更加困难。在我的示例中,我们有一个“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;}
}

现在让我们假设我们重构了这个类,将所有的统计簿记拆分为一个辅助对象,然后让实际的执行者(在可能的最高级别)报告发生了什么。现在 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 原则一直延伸到实现细节。

这都是这个问题的导火索:一旦我完成了这个重构,我的类就变得更难在 spring 上下文文件中使用了。以前我可以只使用一个定义并将同一个对象创建为一个我可以提供(或自动装配)给其他对象的 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 原则。在我深入研究过的弹性代码库中,这种模式一遍又一遍地出现,这些类最终在单个类中实现以支持“高级”spring-context 使用(单个 bean def),而不是进一步分解,因为我们发生这种情况时会失去春天的魔力。

我是 spring 和 java 的相对新手(过去几年一直在 c++、scala 和 ruby​​ 中)所以我可能缺少一些东西。java/spring 开发人员如何处理 spring 可用性和充分考虑的实现之间的这种“阻抗不匹配”?他们是硬着头皮创建 DelegatingBulkyClass,强迫用户构建我重构的 impl 类,还是仅仅坚持使用 BulkyClass 模式,因为它最简单?

4

1 回答 1

0

像 BulkyThingDoer 一样,您可以使用 DoerStatisticHolder 进行注释,@Component这样它将由 spring 管理,然后您可以将其自动连接@Autowire到 ThingDoer。一个缺点是您还需要一个非参数构造函数。

如果你总是需要一个对象的新实例,你可以用@Scope("prototype").

使用基于注释的配置方法来自动装配只有一个实际实现的类可以节省很多 xml 配置开销。

于 2012-07-11T20:34:45.083 回答