2010-11-25 12 views
2

私は私が作成した小さなクラスの検証クラスを用意していますが、ここでは優先順位付きルールを処理するように更新する必要があります。優先度の高いルールが満たされている場合は、ユーザーにメッセージ全体を追加するのではなく、ユーザーに単一のエラーメッセージを伝えるだけなので、それ以上の検証を実行する必要はありません。タイプセーフな優先順位付けされたルール

//Rule.java 
public interface Rule<T> { 
    List<ErrorMessage> validate(T value); 
} 

//ValidationStrategy.java 
public interface ValidationStrategy<T> { 
    public List<Rule<? super T>> getRules(); 
} 

//Validator.java 
public class Validator<T> implements Rule<T> { 

    private List<Rule<? super T>> tests = new ArrayList<Rule<? super T>>(); 

    public Validator(ValidationStrategy<T> type) { 
     this.tests = type.getRules(); 
    } 

    public List<ErrorMessage> validate(T value) { 
     List <ErrorMessage> errors = new ArrayList<ErrorMessage>(); 
      for (Rule<? super T> rule : tests) { 
       errors.addAll(rule.check(value)); 
      } 
      return errors; 
    } 
} 

私は優先順位のルールに対処するには、このコードを変更し、いくつかの問題を抱えています:ここで

は、私が持っているクラスのセットです。確かにルールエンジンを導入するのではなく、使用するように修正できるものがあります。

理想的には、私は、このようなルールを作成することができるだろう:更新されたクラスへ

private static final Rule<SomeClass> ensureAllFieldsNotBlank = new Rule<SomeClass>(RulePriority.HIGHEST) { 

    public List<ErrorMessage> check(SomeClass someClass) { 
     List<ErrorMessage> errors = new ArrayList<ErrorMessage>(); 
     if (StringUtils.isBlank(someClass.getValue1()) 
      && StringUtils.isBlank(someClass.getValue2()) 
      && StringUtils.isBlank(someClass.getValue3())) { 
       errors.add("Provide a response for \"" + someClass.getName() + "\""); 
     } 
     return errors; 
    } 
}; 

編集:ルールの比較を処理するための抽象基本クラスを作成する方法についてどのように

//ValidationStrategy.java 
public interface ValidationStrategy<T> { 
    public List<Rule<? super T>> getRules(RulePriority rulePriority); 
} 

//RulePriority.java 
public enum RulePriority { HIGHEST, DEFAULT, LOWEST; } 

//Validator.java 
public class Validator<T> implements Rule<T> { 
    private List<Rule<? super T>> tests = new ArrayList<Rule<? super T>>(); 
    private ValidationStrategy<T> validationStrategy; 

    public Validator(ValidationStrategy<T> validationStrategy) { 
     this.validationStrategy = validationStrategy; 
     for (RulePriority rp : RulePriority.values()) { 
      this.tests.addAll(validationStrategy.getRules(rulePriority)); 
     } 
    } 

    public List<ErrorMessage> validate(T value) { 
     List<ErrorMessage> errors = new ArrayList<String>(); 
     for (RulePriority rp : RulePriority.values()) { 
      for (Rule<? super T> rule : validationStrategy.getRules(rp)) { 
       errors.addAll(rule.validate(value)); 
      } 
      if (errors.size() > 0) { 
       break; 
      } 
     } 
     return errors; 
    } 

答えて

2

abstract class PrioritizedRule<T> implements Rule<T>, Comparable<PrioritizedRule<T>>{ 
    public int compareTo(PrioritizedRule<T> other){ 
     //Implement something that compares rule priorities here. 
     //This will probably require support from the constructor, which- 
     //since this is abstract- must be Protected. 

そこから、PrioritizedValidator(PrioritizedRuleが必要)は、そのcoll Validateの開始時のルール(ルールのコレクションが最後の検証以降に変更されている場合。それはコレクションをソートするのに適切な時間です。私たちは、すべての変更に対して並べ替えを繰り返すことは望ましくないからです。必要がない場合はソートを行います)、Validateループは、エラーメッセージのリストは、ルールの優先度の間の遷移間で非空である:

public List<ErrorMessage> validate(T value) { 
    if(ruleSetModified){ 
     //be careful: validate becomes unsafe for multithreading here, even if you 
     //aren't modifying the ruleset; if this is a problem, implement locking 
     //inside here. Multiple threads may try to sort the collection, but not 
     //simultaneously. Usually, the set won't be modified, so locking before 
     //the test is much, much slower. Synchronizing the method is safest, 
     //but carries a tremendous performance penalty 
     Collections.sort(rule); 
     ruleSetModified = false; 
    } 
    List <ErrorMessage> errors = new ArrayList<String>(); 
     PrioritizedRule prev = null; 
     for (PrioritizedRule<? super T> rule : tests) { 
      if(prev != null && prev.compareTo(rule) != 0 && !errors.isEmpty()){ 
       return errors; 
      } 
      errors.addAll(rule.check(value)); 
      prev = rule; 
     } 
     return errors; 
} 

私はあなたが「ルール・エンジンに持ち込むことなく、...」によって何を意味するかわからないんだけど、自分自身をソートするためのルールを定義しますおそらく最も優雅なアプローチです。ただし、2つのPrioritizedRuleを互いに比較する必要があります。そのため、インターフェイスの代わりにPrioritizedRuleを抽象ベースにすることをお勧めします。これは、compareTo実装が一貫性を保つために必要な場所であるためです。あなたのコレクションをソート済みのセットに保存しようとしない限り、compareToはequalsと一貫性を持つ必要はありません。これは決してうまくいくことはありません(PrioritizedRuleはequalsとの一貫性を保つのに十分気付くことができません! 。

また、Comparator>を実装してください。ただし、並べ替えるのに十分な情報が公開されるように、Ruleインターフェイスを変更する必要があります。

+0

私はこのアプローチが気に入っていますが、 'compareTo()'を 'equals()'と一貫性を持たせるのは簡単だと指摘したいだけです。等しい場合は0を返し、そうでなければ何も返しません。 – CurtainDog

+0

これは、すべてのPrioritizedRuleオブジェクトがクリアで定義された合計順序を有することを要する。つまり、非常に異なるデータであり、非常に異なるデータであるルールに対しては、PrioritizedRule自体が「タイブレイカー」でなければならない。これは一般的に保証することが不可能に近いです。ハッシュコード(hashCode()!がすべてhashCode()!をオーバーライドした場合)をタイブレイカーとして使用しても、必ずしも安全であるとは限りません。つまり、重要なトータルオーダーがない限り、常に衝突する可能性があります。安全に行うための情報。すべてのルールオブジェクトにtiebreakersとしてのGUIDを強制しますか? –

+0

私は同様のアプローチを考えていましたが、エラーリストを返す前にすべてのルールを優先して実行する必要があります。したがって、2つのルールが最高の優先度を持ち、4がデフォルトの優先度を持ち、2つの最高レベルのルールが合格した場合、4つのルールすべてをデフォルトの優先度で検証する必要があります。 – Scott

関連する問題