3

あるエンティティから別のエンティティへの値のコピーを処理するプロジェクトで、クラスを AttributeUpdater という名前にします。コア メソッドは、エンティティの属性を走査し、指定されたとおりに 2 番目の属性にコピーします。そのループ中に、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);
  }
}

エンティティがまだ永続化されていなくても、レポートが実際に重要なケースが 1 つあります。メソッド シグネチャに別のパラメーターを追加するだけでよいのであれば、これはそれほど大きな問題ではありませんが、クラスの実際の構造と必要な屈折の量のために、それはやや選択肢から外れています。メソッドは静的であるため、私が思いついた他の唯一の解決策は、フラグを静的フィールドとして追加し、関数呼び出しの直前に設定することです。

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 は、スレッドセーフに使用されるコンテナーです。このソリューションは、仕事をしますが、少なくとも 1 つの大きな欠点があります。レポートが削除されることを前提とする他のすべての方法では、それらのレポートが期待どおりに削除されることを保証する方法がありません。ここでも、屈折はオプションではありません。だから私はこれを思いついた:

パブリック クラス 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 に頻繁にアクセスすることです。それらの 1 つに対する反復は、ネストされた要素ごとに disableDeletionForNextCall() を呼び出すことを意味します。また、このメソッドは全体的に頻繁に呼び出されるため、有効なパフォーマンス上の懸念があります。

TL;DR: 疑似 Java ソース コードを見てください。1 つ目は古いコードで、2 つ目と 3 つ目は削除を無効にするための別の試みです。パラメータをメソッド シグネチャに追加することはできません。

どちらのソリューションが優れているかを判断する可能性はありますか、それとも単に哲学的な問題ですか? または、この問題に対するより良い解決策はありますか?

4

1 に答える 1

1

パフォーマンスの点でどちらのソリューションが優れているかを判断する明白な方法は、これをベンチマークすることです。どちらのソリューションも、少なくとも読み取りのためにスレッド ローカル変数にアクセスするため、あまり違いがあるとは思えません。おそらく、次のように組み合わせることができます。

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

この場合、不要な書き込みを行わずに、2 番目の解決策 (フラグのリセットを保証する) の利点が得られます。

実質的な違いはあまりないと思います。運が良ければ、HotSpot JVM はスレッド ローカル変数を適切なネイティブ コードにコンパイルします。これは、パフォーマンスをあまり低下させずに動作しますが、実際の経験はありません。

于 2012-09-02T22:33:27.790 に答える