2014-05-01 4 views
9

Ich muss einen Prozess erstellen, der einen Datensatz gegen ca. 200 Validierungsregeln validiert. Ein Datensatz kann einer von ~ 10 Typen sein. Es gibt einige Segmentierungen von Validierungsregeln zu Record-Typen, aber es gibt viele Überschneidungen, die verhindern, dass die Validierungsregeln sauber gruppiert werden.Für eine große Validierungsaufgabe ist die Kette der Verantwortungsmuster eine gute Wette?

Während meines Entwurfs betrachte ich eine Kette von Verantwortungsmustern für alle Validierungsregeln. Ist das eine gute Idee oder gibt es ein besseres Designmuster?

+0

Es ist definitiv nicht eine schlechte Wette. Haben Sie daran gedacht, für jeden Typ separate Ketten oder für alle Typen eine einzelne Kette zu erstellen? – Kayaman

+0

Eine einzelne Kette für alle Typen (weil die Regeln nicht gut segmentieren), aber testen, ob sie für den Datensatz gilt. Ich dachte, dies würde auch das Hinzufügen neuer Regeln erleichtern. (Lesen: einfacher für den Kunden zu verstehen ...) – yamori

+1

Haben Sie erwogen, Drools zu benutzen? –

Antwort

4

Die Verantwortungskette beinhaltet, dass es eine Reihenfolge gibt, in der die Validierungen stattfinden müssen. Ich würde wahrscheinlich etwas Ähnliches wie das Strategie-Muster verwenden, wo Sie eine Reihe von Validierungsstrategien haben, die auf einen bestimmten Datensatztyp angewendet werden. Sie könnten dann eine Fabrik verwenden, um den Datensatz zu überprüfen und den richtigen Satz von Validierungen anzuwenden.

+0

CoR erlaubt die Bestellung von Abhängigkeiten, aber es ist in keiner Weise erforderlich, um das Muster zu verwenden. – Kayaman

+0

Also das Wort "impliziert". Dies ist im Wesentlichen eine meinungsbezogene Frage. Ich habe meine Meinung geäußert. – jgitter

13

Validierung ist häufig ein zusammengesetztes Muster. Wenn Sie es brechen, wollen Sie die trennen, was Sie die zu wollen, wie aus wollen Sie es tun, erhalten Sie:

Wenn foo gültig ist dann etwas tun.

Hier haben wir die Abstraktion ist gültig - Vorbehalt: Dieser Code wurde aus dem aktuellen, ähnliche Beispiele aufgehoben, so dass Sie fehlende Symbolik und so finden können. Aber das ist so, damit Sie das Bild bekommen. Zusätzlich enthält das Objekt Meldungen über den Fehler sowie einen einfachen Status (true/false). Dies ermöglicht Ihnen die Option, einfach zu fragen: "Hat es bestanden?" vs. „Wenn es versäumt, sag mir warum“

QuickCollection 

und

QuickMap 

Convenience-Klassen sind für jede Klasse und sie schnell in diese respektiert Arten drehen, indem lediglich auf einen Delegierten zuweisen. In diesem Beispiel bedeutet dies, dass Ihr zusammengesetzter Validierer bereits eine Sammlung ist und z. B. iteriert werden kann.

Sie hatten ein sekundäres Problem in Ihrer Frage: "sauber binden" wie in "Typ A" -> Regeln {a, b, c} "und" Typ B "-> Regeln {c, e, z} "

Dies ist leicht mit einer Karte verwaltet. Nicht ganz eine Command pattern aber in der Nähe

Map<Type,Validator> typeValidators = new HashMap<>(); 

Einrichtung der Validator für jeden Typen dann eine Zuordnung zwischen Typen erstellen.Das ist wirklich am besten als Bean Config getan, wenn Sie mit Hilfe von Java, aber Definitiv Verwendung dependency injection

public interface Validator<T>{ 


    public Result validate(T value); 


    public static interface Result { 

     public static final Result OK = new Result() { 
      @Override 
      public String getMessage() { 
       return "OK"; 
      } 

      @Override 
      public String toString() { 
       return "OK"; 
      } 

      @Override 
      public boolean isOk() { 
       return true; 
      } 
     }; 

     public boolean isOk(); 

     public String getMessage(); 
    } 
    } 

nun einige einfache Implementierungen, den Punkt zeigen:

public class MinLengthValidator implements Validator<String> { 

private final SimpleResult FAILED; 

private Integer minLength; 

public MinLengthValidator() { 
    this(8); 
} 

public MinLengthValidator(Integer minLength) { 
    this.minLength = minLength; 
    FAILED = new SimpleResult("Password must be at least "+minLength+" characters",false); 
} 

@Override 
public Result validate(String newPassword) { 
    return newPassword.length() >= minLength ? Result.OK : FAILED; 
} 

@Override 
public String toString() { 
    return this.getClass().getSimpleName(); 
} 
} 

Hier ist ein weiteres wir kombinieren mit

public class NotCurrentValidator implements Validator<String> { 

    @Autowired 
    @Qualifier("userPasswordEncoder") 
    private PasswordEncoder encoder; 

    private static final SimpleResult FAILED = new SimpleResult("Password cannot be your current password",false); 

    @Override 
    public Result validate(String newPassword) { 
     boolean passed = !encoder.matches(newPassword,user.getPassword()); 
     return (passed ? Result.OK : FAILED); 
    } 

    @Override 
    public String toString() { 
     return this.getClass().getSimpleName(); 
    } 

} 

Jetzt ist hier ein Verbund:

public class CompositePasswordRule extends QuickCollection<Validator> implements Validator<String> { 


public CompositeValidator(Collection<Validator> rules) { 
    super.delegate = rules; 
} 

public CompositeValidator(Validator<?>... rules) { 
    super.delegate = Arrays.asList(rules); 
} 



@Override 
public CompositeResult validate(String newPassword) { 
    CompositeResult result = new CompositeResult(super.delegate.size()); 
    for(Validator rule : super.delegate){ 
     Result temp = rule.validate(newPassword); 
     if(!temp.isOk()) 
      result.put(rule,temp); 
    } 

    return result; 
} 


    public static class CompositeResult extends QuickMap<Validator,Result> implements Result { 
     private Integer appliedCount; 

     private CompositeResult(Integer appliedCount) { 
      super.delegate = VdcCollections.delimitedMap(new HashMap<PasswordRule, Result>(), "-->",", "); 
      this.appliedCount = appliedCount; 
     } 

     @Override 
     public String getMessage() { 
      return super.delegate.toString(); 
     } 

     @Override 
     public String toString() { 
      return super.delegate.toString(); 
     } 

     @Override 
     public boolean isOk() { 
      boolean isOk = true; 
      for (Result r : delegate.values()) { 
       isOk = r.isOk(); 
       if(!isOk) 
        break; 
      } 
      return isOk; 
     } 
     public Integer failCount() { 
      return this.size(); 
     } 

     public Integer passCount() { 
      return appliedCount - this.size(); 
     } 
    } 
} 

und nun ein Ausschnitt der Nutzung:

private Validator<String> pwRule = new CompositeValidator<String>(new MinLengthValidator(),new NotCurrentValidator()); 

Validator.Result result = pwRule.validate(newPassword); 
if(!result.isOk()) 
    throw new PasswordConstraintException("%s", result.getMessage()); 

user.obsoleteCurrentPassword(); 
user.setPassword(passwordEncoder.encode(newPassword)); 
user.setPwExpDate(DateTime.now().plusDays(passwordDaysToLive).toDate()); 
userDao.updateUser(user); 
+0

Ordentlich! Kann ich daraus einen Kern machen? – Jay

+0

Natürlich. Setzen Sie einfach einen Link zurück –

+0

[Hier] (https://github.com/JExaples/quicklooks/tree/master/Chain-of-Responsibility) Sie gehen, Kumpel. Planen Sie die Weiterentwicklung weiter? Ich hatte heute ein ähnliches Problem und ich dachte, ich sollte es versuchen und eine Bibliothek oder etwas machen, das ich in meinen späteren Projekten verwenden kann. – Jay