3

我们有一个类,在我们的项目中将其命名为 AttributeUpdater,用于处理将值从一个实体复制到另一个实体。核心方法遍历一个实体的属性并将它们按指定复制到第二个中。在该循环期间,AttributeUpdater 将所有报告(其中包含有关在复制期间被覆盖的值的信息)收集到一个很好的列表中,以用于最终记录目的。如果值被覆盖的旧实体从未保存到数据库中,则删除此列表,因为在这种情况下,您只会覆盖默认值和被认为是多余的日志记录。在伪 Java 代码中:

public class AttributeUpdater {
  public static CopyResult updateAttributes(Entity source, Entity target, String[] attributes) {
    List<CopyReport> reports = new ArrayList<CopyReport>();
    for(String attribute : attributes) {
          reports.add(copy(source, target, attribute));
    }
    if(target.isNotPersisted()) {
      reports.clear();
    }
    return new CopyResult(reports);
  }
}

现在有人顿悟了,在这种情况下,即使实体尚未持久化,报告实际上也很重要。如果我可以在方法签名中添加另一个参数,这不会有什么大不了的,但由于类的实际结构和所需的折射量,这在某种程度上是不可选择的。由于该方法是静态的,我想出的唯一其他解决方案是将标志添加为静态字段并在函数调用之前设置它。

public class AttributeUpdater {

  public static final ThreadLocal<Boolean> isDeletionEnabled = new ThreadLocal<Boolean> {
      @Override protected Boolean initialValue() {
             return Boolean.TRUE;
      }      

  public static Boolean getDeletionEnabled() { return isDeletionEnabled.get(); }
  public static void setDeletionEnabled(Boolean b) { isDeletionEnabled.set(b); }

  public static CopyResult updateAttributes(Entity source, Entity target, String[] attributes) {
    List<CopyReport> reports = new ArrayList<CopyReport>();
    for(String attribute : attributes) {
          reports.add(copy(source, target, attribute));
    }
    if(isDeletionEnabled.get() && target.isNotPersisted()) {
      reports.clear();
    }
    return new CopyResult(reports);
  }
}

ThreadLocal 是一个用于线程安全的容器。这个解决方案虽然可以完成工作,但至少对我来说有一个主要缺点:对于所有其他假设报告已删除的方法,现在无法保证这些报告将按预期删除。再次折射不是一种选择。所以我想出了这个:

公共类 AttributeUpdater {

  private static final ThreadLocal<Boolean> isDeletionEnabled = new ThreadLocal<Boolean> {
      @Override protected Boolean initialValue() {
             return Boolean.TRUE;
      }      

  public static Boolean getDeletionEnabled() { return isDeletionEnabled.get(); }
  public static void disableDeletionForNextCall() { isDeletionEnabled.set(Boolean.FALSE); }

  public static CopyResult updateAttributes(Entity source, Entity target, String[] attributes) {
    List<CopyReport> reports = new ArrayList<CopyReport>();
    for(String attribute : attributes) {
          reports.add(copy(source, target, attribute));
    }
    if(isDeletionEnabled.get() && target.isNotPersisted()) {
      reports.clear();
    }
    isDeletionEnabled.set(Boolean.TRUE);
    return new CopyResult(reports);
  }
}

这样,我可以保证对于旧代码,该函数将始终像更改前一样工作。这个解决方案的缺点是,特别是对于嵌套实体,我将经常访问 ThreadLocal-Container - 迭代其中一个意味着为每个嵌套元素调用 disableDeletionForNextCall()。此外,由于该方法被大量调用,因此存在有效的性能问题。

TL;DR:查看伪 Java 源代码。第一个是旧代码,第二个和第三个是允许删除禁用的不同尝试。参数不能添加到方法签名中。

是否有可能确定哪种解决方案更好,或者这仅仅是一个哲学问题?还是有更好的解决方案来解决这个问题?

4

1 回答 1

1

决定哪种解决方案在性能方面更好的明显方法是对此进行基准测试。由于两种解决方案都访问线程局部变量至少是为了读取,我怀疑它们会相差太大。你也许可以像这样组合它们:

if(!isDeletionEnabled.get())
  isDeletionEnabled.set(Boolean.TRUE);
else if (target.isNotPersisted())
  reports.clear();

在这种情况下,您将受益于第二种解决方案(保证重置标志)而无需进行不必要的写入。

我怀疑会有很大的实际差异。运气好的话,HotSpot JVM 会将线程局部变量编译成一些不错的本机代码,虽然我没有实际经验,但它在工作时不会有太多的性能损失。

于 2012-09-02T22:33:27.790 回答