Validierung mit reinem Java

Die Idee von eingeschränkten Eigenschaften in Java ist nicht neu. Seit JDK 1.1 hat eine Legion von JavaBeans-Entwicklern ein leistungsstarkes Framework aus dem java.beansPaket verwendet, um Datenvalidierungsregeln durchzusetzen. Leider versuchen die anderen von uns, die nicht in der Lage sind, "wiederverwendbare Softwarekomponenten herzustellen, die in einem Builder-Tool visuell manipuliert werden können", häufig das Rad neu zu erfinden, indem sie den Java-Kernansatz zugunsten verschiedener proprietärer Komponenten aufgeben Lösungen. Die Externalisierung von Datenvalidierungsregeln ist der Bereich, in dem Sie die größte Kreativität sehen können. Ein interessanter XML-basierter Ansatz wurde kürzlich in der Reihe "Validierung mit Java und XML-Schema" von JavaWorld vorgeschlagen. Während ich die XML-Technologie bewundere, glaube ich, dass Java über alles verfügt, um das Problem auf eleganteste Weise zu lösen. Um es Ihnen zu zeigen, lade ich Sie ein, einige der echten Juwelen des java.beansPakets wiederzuentdecken . Sie werden Ihnen helfen, ein paar nützliche Klassen aufzubauen und ein paar interessante Tricks zu lernen.

Die Logik hinter eingeschränkten Eigenschaften in Java ist recht einfach. Bevor ein neuer Datenwert akzeptiert wird, stellt das Objekt (Eigentümer der eingeschränkten Eigenschaft) sicher, dass er von allen interessierten Parteien akzeptiert wird, die ein Veto einlegen können. Ein solcher "Antrag auf Genehmigung" wird jedem Registrierten java.beans.VetoableChangeListenerin Form eines java.beans.PropertyChangeEventObjekts zugestellt. Wenn ein oder mehrere Vetos ausgestellt wurden, wird der vorgeschlagene Datenwert abgelehnt. Ein Veto wird von a java.beans.PropertyVetoException. Im Allgemeinen ist die Komplexität der von diesen Zuhörern erzwungenen Regeln unbegrenzt und hängt nur von den Anforderungen des Projekts und der Kreativität des Entwicklers ab. In dieser Übung lernen Sie, wie Sie mit den allgemeinsten Datenvalidierungskriterien umgehen, die Sie auf die meisten Objekte anwenden können.

Lassen Sie uns zunächst eine Klasse erstellen, die BusinessObjectmit einer einfachen numerischen Eigenschaft aufgerufen wird . Als nächstes werde ich zeigen, wie Sie die Eigenschaft in eine eingeschränkte ändern können, um zu sehen, wie sie sich von der einfachen unterscheidet.

öffentliche Klasse BusinessObject {private int numericValue; public void setNumericValue (int newNumericValue) {numericValue = newNumericValue; } public int getNumericValue () {return numericValue; }}

Bitte beachten Sie, dass die einzige Eigenschaft dieser Klasse stillschweigend jeden Wert des richtigen Typs akzeptiert. Um dies einzuschränken, muss ein Veto-Ereignis erstellt und an die Verbraucher verteilt werden. Eine so genannte Utility-Klasse java.beans.VetoableChangeSupportwurde speziell entwickelt, um diese Verantwortlichkeiten zu erfüllen. Eine neue Klasse namens ConstrainedObjectwird alle Arrangements mit dieser Gebrauchsklasse verwalten und somit ein guter, liebevoller Elternteil für die BusinessObject. Apropos Verbraucher, wir müssen eine weitere benutzerdefinierte Klasse namens erstellenValidatordas hostet generische Datenvalidierungsalgorithmen. Es wird der einzige Verbraucher der Veto-Ereignisse in unserem Rahmen sein. Dieser vereinfachte Ansatz weicht von den Regeln des JavaBeans-Spiels ab (Einzelheiten finden Sie in der JavaBeans-API-Spezifikation). Dazu muss jede eingeschränkte Eigenschaft als gebundene Eigenschaft dargestellt und die Behandlung mehrerer Listener unterstützt werden. Diese Abweichung ist durchaus akzeptabel, da Sie hier keine JavaBean erstellen, aber dennoch erwähnenswert.

importiere java.beans. *; / ** * Die Verantwortung des ConstrainedObject besteht darin, generische Validierungsverantwortlichkeiten für die Dateneingabe * an den {@link Validator} zu delegieren und * Unterklassen mit einer transparenten Schnittstelle zu versehen. * / public class ConstrainedObject {private VetoableChangeSupport vetoableSupport = new VetoableChangeSupport (this); / ** * Erstellt ein neues Objekt mit dem generischen Eigenschaftsvalidator * / public ConstrainedObject () {vetoableSupport.addVetoableChangeListener (new Validator ()); } / ** * Diese Methode wird von Unterklassen verwendet, um einen neuen Wert in * den eingeschränkten Eigenschaften des Typs int zu validieren. Es kann leicht überladen werden *, um auch mit anderen primitiven Typen umzugehen. * @param propertyName Der programmatische Name der Eigenschaft, die geändert werden soll. * @param oldValue Der alte Wert der Eigenschaft.* @param newValue Der neue Wert der Eigenschaft. * / protected void validate (String propertyName, int oldValue, int newValue) löst PropertyVetoException aus {vetoableSupport.fireVetoableChange (propertyName, neue Ganzzahl (oldValue), neue Ganzzahl (newValue)); }}

Um den Code kompakt zu halten, ConstrainedObjectbietet das eine Methode, mit der intnur Eigenschaften überprüft werden. Sie können diese Methode auch für die anderen Typen leicht überladen. Wenn Sie dies tun, stellen Sie einfach sicher, dass alle primitiven Typen in ihre jeweiligen Wrapper eingeschlossen werden, da PropertyChangeEventalte und neue Werte als Referenztypen geliefert werden. Jetzt können Sie eine zweite Überarbeitung des BusinessObject:

importiere java.beans. *; / ** * Diese Beispielklasse verwendet tatsächlich Datenvalidierungsdienste, die in diesem Framework definiert sind. * / public class BusinessObject erweitert ConstrainedObject {private int numericValue; public void setNumericValue (int newNumericValue) löst PropertyVetoException aus {// vorgeschlagenen Wert validieren validate ("numericValue", numericValue, newNumericValue); // Der neue Wert wird genehmigt, da keine Ausnahmen ausgelöst wurden. numericValue = newNumericValue; } public int getNumericValue () {return numericValue; }}

Wie Sie sehen, sieht die Setter-Methode jetzt etwas komplizierter aus als zuvor. Es ist ein kleiner Preis für eine eingeschränkte Immobilie. Jetzt müssen Sie eine erstellen Validator, was der interessanteste Teil dieser Übung ist. Hier erfahren Sie, wie Sie Datenüberprüfungsregeln nur mit reinem Java externalisieren.

importiere java.beans. *; public class Validator implementiert VetoableChangeListener {public void vetoableChange (PropertyChangeEvent evt) löst PropertyVetoException aus {// hier die Validierung durchführen}}

Dieses Codeskelett ist nur ein Ausgangspunkt, zeigt jedoch, was zu Beginn des Validierungsprozesses verfügbar ist. Überraschenderweise hat es viele Informationen. Hier PropertyChangeEventerfahren Sie mehr über die Objektquelle der eingeschränkten Eigenschaft, den Namen der Eigenschaft selbst sowie die vorhandenen und vorgeschlagenen Werte. Mit diesen Informationen können Sie die Kraft der Selbstbeobachtung nutzen , um sich nach den zusätzlichen Informationen der Unterkunft zu erkundigen. Nach welchen Informationen sollten Sie suchen? Validierungsregeln natürlich! Eine Klasse java.beans.Introspectorwurde ausschließlich zum Sammeln zusätzlicher Informationen über eine Zielklasse entwickelt. Die Informationen werden in Form eines java.beans.BeanInfoObjekts bereitgestellt , das Sie in diesem Beispiel mit einem einfachen Aufruf wie folgt anfordern können:

BeanInfo info = Introspector.getBeanInfo (evt.getSource (). GetClass ()); 

Die Introspectorkönnen BeanInfoauf zwei Arten sammeln . Zunächst wird versucht, explizite Informationen über die Zielklasse abzurufen, indem nach einer Klasse mit demselben Namen mit Ausnahme ihres BeanInfoSuffix gesucht wird . In diesem Fall wird nach einer Klasse mit dem Namen gesucht BusinessObjectBeanInfo. Wenn eine solche Klasse aus irgendeinem Grund nicht verfügbar ist, Introspectorversucht sie, ein BeanInfoObjekt mithilfe einer Reflexion auf niedriger Ebene dynamisch zu erstellen .

Obwohl dies ein faszinierendes Thema ist, ist die implizite Selbstbeobachtung offensichtlich nicht das, worauf Sie sich bei Ihrer Suche nach Validierungsregeln verlassen werden. Eine großartige Sache an der BeanInfoKlasse ist ihre Beziehung zur Zielklasse, die als "steinharte lose Kopplung" definiert werden kann. Das Fehlen von Referenzen aus der Zielklasse macht die Kopplung sehr locker. Sollten sich die Validierungsregeln ändern, müssen Sie nur die BeanInfoKlasse aktualisieren und neu kompilieren , ohne die Zielklasse zu beeinträchtigen. Klar definierte Namensregeln, die in der Java-Kern-API dokumentiert sind, machen das Kompilieren absolut solide. Hier BeanInfoschlägt der Ansatz der Regelexternalisierung mit seinen proprietär implementierten Gegenstücken zweifellos.

Bevor Sie mit dem Erstellen der expliziten BeanInfoKlasse beginnen, muss ich erwähnen, dass Sie sie mit einer Vielzahl von Deskriptoren laden können, die die Klasse selbst oder ihre Methoden und Ereignisse betreffen, nicht nur die Eigenschaften. Dank der Entwickler von Java müssen Sie nicht alle diese Informationen bereitstellen. Der Introspectornimmt auf, was explizit verfügbar ist, und führt dann seine implizite Analysemagie aus. Daher können Sie sich nur auf die Eigenschaftsbeschreibungen konzentrieren.

Um die Datenüberprüfungsregeln einer Eigenschaft zu beschreiben, verwenden Sie die java.beans.PropertyDescriptorKlasse. Wenn Sie sich die Dokumentation ansehen, werden Sie feststellen, dass diese Klasse die Möglichkeit hat, alle erdenklichen Informationen über die Eigenschaft bereitzustellen, einschließlich Datenvalidierungsregeln! Mit dieser setValueMethode definieren Sie die Regeln als eine Reihe benannter Attribute. So können Sie die Grenzen für die numerische Eigenschaft einschränken numericValue:

PropertyDescriptor _numericValue = neuer PropertyDescriptor ("numericValue", targetClass, "getNumericValue", "setNumericValue"); _numericValue.setValue ("maxVal", neue Ganzzahl (100)); _numericValue.setValue ("minVal", neue Ganzzahl (-100));

Der einzige Schwachpunkt hierbei ist, dass Sie sich nicht auf die Leistung des Java-Compilers verlassen können, um die Rechtschreibung der Attributnamen zu überprüfen. Die Verwendung eines vordefinierten Satzes von Konstanten kann dies leicht beheben. Die Klasse Validatorist ein guter Kandidat für das Hosten solcher Konstanten:

public static final String MAX_VALUE = "maxValue"; public static final String MIN_VALUE = "minValue";

Jetzt können Sie dieselben Regeln sicherer umschreiben. Lassen Sie uns vor diesem Hintergrund eine endgültige Überarbeitung der BeanInfoKlasse erstellen :

importiere java.beans. *; öffentliche Klasse BusinessObjectBeanInfo erweitert SimpleBeanInfo {Klasse targetClass = BusinessObject.class; public PropertyDescriptor [] getPropertyDescriptors () {try {PropertyDescriptor numericValue = neuer PropertyDescriptor ("numericValue", targetClass, "getNumericValue", "setNumericValue"); numericValue.setValue (Validator.MAX_VALUE, neue Ganzzahl (100)); numericValue.setValue (Validator.MIN_VALUE, neue Ganzzahl (-100)); PropertyDescriptor [] pds = new PropertyDescriptor [] {numericValue}; pds zurückgeben; } catch (IntrospectionException ex) {ex.printStackTrace (); return null; }}}

Schließlich führen Sie das aus, Validatordamit die Informationen abgerufen und analysiert werden können. Es wird eine praktische Methode zum Abrufen eines PropertyDescriptoraus dem BeanInfoprogrammatischen Namen der Eigenschaft verwendet. Falls die Eigenschaft aus irgendeinem Grund nicht gefunden wurde, benachrichtigt die Methode den Aufrufer mit einer selbsterklärenden Ausnahme.

importiere java.beans. *; / ** * Diese Klasse implementiert einen generischen Datenvalidierungsmechanismus, der auf den in der BeanInfo-Klasse definierten externen * Regeln basiert. * / public class Validator implementiert VetoableChangeListener {public static final String MAX_VALUE = "maxValue"; public static final String MIN_VALUE = "minValue"; / ** * Die einzige Methode, die für die Schnittstelle {@link VetoableChangeListener} erforderlich ist. * / public void vetoableChange (PropertyChangeEvent evt) löst PropertyVetoException aus {try {// hier hoffen wir, explizit definierte zusätzliche Informationen // über die Objektquelle der eingeschränkten Eigenschaft BeanInfo info = Introspector.getBeanInfo (evt.getSource (). getClass abzurufen ()); // finde einen Eigenschaftsdeskriptor anhand des angegebenen Eigenschaftsnamens PropertyDescriptor descriptor = getDescriptor (evt.getPropertyName (), info);Integer max = (Integer) descector.getValue (MAX_VALUE); // prüfe ob der neue Wert größer als erlaubt ist wenn (max! = null && max.compareTo (evt.getNewValue ()) 0) {// beschwere dich! neue PropertyVetoException auslösen ("Wert" + evt.getNewValue () + "ist kleiner als das zulässige Minimum" + min, evt); }} catch (IntrospectionException ex) {ex.printStackTrace (); }} / ** * Diese Dienstprogrammmethode versucht, einen PropertyDescriptor aus dem BeanInfo-Objekt mit dem angegebenen Eigenschaftsnamen abzurufen. * @param name der programmatische Name der Eigenschaft * @param info das Bean-Info-Objekt, das nach dem Eigenschaftsdeskriptor durchsucht wird. * @throws IllegalArgumentException, wenn im angegebenen * BeanInfo-Objekt keine Eigenschaft mit angegebenem Namen vorhanden ist. * / private PropertyDescriptor getDescriptor (String name,BeanInfo info) löst IllegalArgumentException aus {PropertyDescriptor [] pds = info.getPropertyDescriptors (); für (int i = 0; i
   
    

Ob Sie es glauben oder nicht, das ist es! Sie haben jetzt alle Teile installiert und können Ihre Datenvalidierung testen. Dieser einfache Treiber macht genau das: