# Framework-unabhängige Validierung



## Guest (21. Jul 2008)

Hallo,

es gibt eine ganze Menge Ansätze Eingabedaten zu validieren. Oft sind diese Ansätze Teil eines größeren Frameworks wie JSF, Spring MVC, Hibernate usw. Der Nachteil ist aber, dass man sich damit mehr oder weniger stark an ein Framework bindet. Die Frage ist, wie man Eingabedaten prüfen kann, so dass man möglichst unabhängig von einem Framework ist? Oder ist das überhaupt sinnvoll?

Wenn set-Methoden nicht so dumm sein sollen, wie sie meistens sind, sondern die Kapselung mit einer Zugriffsmethode auch einen Zweck erfüllen soll, wäre die Prüfung der Eingabedaten an dieser Stelle genau richtig aufgehoben. Die Zugriffsmethode sorgt dafür, dass nur gültige Werte akzeptiert werden und sorgt so dafür, dass das Objekt immer in einem brauchbaren Zustand ist. Außerdem gehören die Bedingungen, die Eingabewerte erfüllen müssen, ja schon inhaltlich in eine Klasse des Modells. Dass sich diese Philosophie nicht mit (dem Murks von) JSF verträgt, ist offensichtlich.

Fehlermeldungen im Falle negativer Prüfung der Eingabedaten ist im Java-Bean-Standard aber offensichtlich nicht vorgesehen, sonst würde die Beans wohl einen Fehlercode statt "void" zurückgeben. 

Meine Anforderung ist, dass die Prüfung in der Modell-Klasse stattfindet und dass ein Fehlercode zurückgegeben wird, der sich dann für die Internationalisierung nutzen lässt, die aber selbst nicht mehr Teil der Prüfung ist bzw. sein sollte.


Ich stelle mir das etwa so vor

```
public class Artikel {
   float preis;
  
   public String setPreis(float preis){
     if (preis < 0.0)
       return Fehler.FEHLERCODE_XY;
  }

}
```

Soweit ich weiß, wäre das dann aber keine Java-Bean nach dem Standard mehr und es könnte an anderen Stellen vielleicht Probleme geben. 

Vielleicht ist der Framework-unabhängige Ansatz auch nur in der Theorie schön und man macht sich in der Praxis unnötig arbeit. Ich stelle mir eine Software-Komponente vor, die man z. B. in einer Webanwendung aber auch in einer Desktop-Anwendung benutzen könnte. In beiden Fällen ist eine Validierung notwendig, aber wenn die Prüfung der Eingabedaten an ein Web-Framework (wie JSF) gekoppelt ist, könnte die Validierung nicht Teil des universell einsetzbaren Moduls sein.

Was meint ihr dazu? Wie könnte man die Valdierung sinnvoll, Framwork-unabhängig machen und ist das überhaupt in der Praxis sinnvoll machbar?


----------



## Gast (21. Jul 2008)

Hallo,

Ein kurzer Tip:

https://springmodules.dev.java.net/docs/reference/0.6/html_single/#beanValidator

Blaetter einfach direkt zu der Beschreibung der Annotations...


Habe selber allerdings keine Erfahrungen mit dem Module und du musst Spring benutzen, wobei das aus meiner Sicht zu verschmerzen ist...


----------



## deamon (21. Jul 2008)

Der Ansatz von "Bean Validator" sieht interesant aus, aber irgendwie erscheint es mir auch relativ kompliziert - jedenfalls erscheint mir die Validierung von Spring selbst einfacher.

Das Validierungsframework von Spring sieht auch schon so aus, wie das Gesuchte. Ich frage mich nur, warum man den Validator in eine eigene Klasse auslagern sollte. Das ist aus meiner Sicht nur in Fällen sinnvoll, wo ein Validator für mehrere Klassen eingesetzt werden kann, was aber nur äußerst selten der Fall sein dürfte - und dann kann man es ja tun. Aber es zwingt einen ja auch niemand, die Implementierung der Prüfung von der zu prüfenden Klasse zu trennen, mich wundert nur, dass das in den Beispielen so vorexerziert wird, ohne das zu hinterfragen.


----------



## ps (21. Jul 2008)

Du kannst dir auch mal den validator aus apache commons anschauen:
http://commons.apache.org/validator/

In Struts2 wird dieser zB. verwendet - hier wurden auch Annotationen hinzugefügt um die Validierung vorzunehmen... man sollte commons-validator eher als solide basis sehen auf welche man eigene validierungsszenarien aufbauen kann.


----------



## Guest (22. Jul 2008)

Anonymous hat gesagt.:
			
		

> Hallo,
> 
> es gibt eine ganze Menge Ansätze Eingabedaten zu validieren. Oft sind diese Ansätze Teil eines größeren Frameworks wie JSF, Spring MVC, Hibernate usw. Der Nachteil ist aber, dass man sich damit mehr oder weniger stark an ein Framework bindet. Die Frage ist, wie man Eingabedaten prüfen kann, so dass man möglichst unabhängig von einem Framework ist? Oder ist das überhaupt sinnvoll?
> 
> Wenn set-Methoden nicht so dumm sein sollen, wie sie meistens sind, sondern die Kapselung mit einer Zugriffsmethode auch einen Zweck erfüllen soll, wäre die Prüfung der Eingabedaten an dieser Stelle genau richtig aufgehoben. Die Zugriffsmethode sorgt dafür, dass nur gültige Werte akzeptiert werden und sorgt so dafür, dass das Objekt immer in einem brauchbaren Zustand ist. Außerdem gehören die Bedingungen, die Eingabewerte erfüllen müssen, ja schon inhaltlich in eine Klasse des Modells. Dass sich diese Philosophie nicht mit (dem Murks von) JSF verträgt, ist offensichtlich.


Ich verwende die frühzeitige Validierung, die von Frameworks angeboten wird ausschließlich für syntaktische Fehler (falsches Datumsformat, String statt Long...) um dem Benutzer frühzeitig ein Feedback über die groben Fehler zu geben. Die Falscheingabe kann bei Web-Anwendungen im Gegensatz zu z.B. einer Swing-GUI nicht bereits bei der Eingabe geprüft werden (zumindest nicht ohne JS).

Fehler, die unabhängig vom GUI-Typ zu validieren sind, werden in einem weiteren Schritt im Model geprüft um möglichst doppelten Code zu vermeiden, allerdings nicht im Setter, denn es können z.B. auch Fehler im Zusammenhang mit anderen Feldern auftreten - z.B. Wert A darf nicht < 0 sein, wenn Wert B > 100 ist. Bei Validierung im Setter hätte man dann schon 3 Stellen an denen überprüft wird, schon wird die ganze Validierungsgeschichte unübersichtlich. 

Treten Fehler bei der Validierung im Model auf, wird eine Exception geworfen. Diese wird dann vom Controller, des jeweiligen GUI-Typs abgefangen.


----------



## HLX (22. Jul 2008)

Letzer Gast war ich. Login vergessen.  :roll:


----------



## Neuer Gast (22. Jul 2008)

Hallo,



> Treten Fehler bei der Validierung im Model auf, wird eine Exception geworfen. Diese wird dann vom Controller, des jeweiligen GUI-Typs abgefangen.



Ich empfinde Exception fuer die Validierung zu verwenden etwas fragwuerdig. Validierungen sind fuer mich ein Teil der Businesslogik und KEINE Behandlung von aussergewöhnlichen Umständen.

Bsp:

Eine Benutzerverwaltung in der der Nutzername eindeutig sein muss.

Useradministration(Service):

- istNutzerHinzufuegbar(user:User): boolean (oder Interface mit speziellen Methoden)

- nutzerHinzufuegen(user:User) :void


```
public void nutzerHinzufuegen(User user) {
    fachlicher Code
    assert .... // Pruefen. ob Nachbedinung erfuellt. Statt "Assert" waere hier auch eine Runtimeexception denkbar
}
```

Damit kann man beim Aufruf pruefen, ob eine Methode ausgefuehrt werden kann oder nicht und dementsprechend reagieren. Falls ein Entwickler die Methode mit invaliden Werten aufruft, ist durch die Assert/Exception sichergestellt, dass keine Invaliden Zustaende "auftretten". 

ps: Exception fuer Validierung erinnern mich immer an:


```
String value = "wert";
try {
    Integer.parseInt(value);
} catch (NumberFormatException e) {
    System.out.println("Es ist ein Fehler aufgetretten");
}
```


----------



## HLX (22. Jul 2008)

Ok. Exceptions machen vor allem Sinn, wenn die Validierung nicht Kern der Methode sondern Bestandteil einer größeren Abarbeitung sind, z.B. wenn man die Werte direkt an eine Kalkulationsmethode übergibt und innerhalb dieser Validierungen vorgenommen werden. In diesem Falle kann ein auftretender Fehler als Ausnahme angesehen werden. 

Bei Verwendung einer speziellen Validierungs-Methode würde ich auch eher eine Fehlerliste an den Controller zurückgeben.


----------



## ps (22. Jul 2008)

Neuer Gast hat gesagt.:
			
		

> Ich empfinde Exception fuer die Validierung zu verwenden etwas fragwuerdig. Validierungen sind fuer mich ein Teil der Businesslogik und KEINE Behandlung von aussergewöhnlichen Umständen.



Ja, ich empfinde das in der Regel auch als fragwürdig. Allerdings ist es IMHO dennoch die eleganteste Variante, vor allem dann wenn man eine Schnittstelle bedient. Für diese Schnittstelle _ist_ es ein aussergewöhnlicher Zustand wenn sie ihre Aufgabe nicht erfüllen kann.



> Eine Benutzerverwaltung in der der Nutzername eindeutig sein muss.
> 
> Useradministration(Service):
> 
> ...



Gutes Beispiel. ABER dennoch _sehr_ problematisch in verteilten Anwendungen. Einen booleschen Wert zurückzugeben reicht auch meist nicht - spontan fällt mir ein: Nutzername vergeben, eMail existiert schon, passwort invalid, benutzername invalid, usw. Jetzt kann man das natürlich Teil der Schnittstellendefinition machen und die validierung komplett dem aufrufenden Code überlassen. Das geht aber nur gut wenn es überschaubar bleibt wer diese Schnittstelle benutzt....

Das weitaus größere Problem stellt dar das in der Zeit zwischen meiner Prüfung (istNutzerHinzufuegbar) und dem Aufruf (nutzerHinzufuegen) sich die Kondition bereits geändert haben kann. zB. hat sich ein anderer Benutzer mit diesem Namen registriert. Die Anwendung schmeisst dann einen Fehler per "assert" und der aufrufende Code ist nicht in der Lage dies abzufangen. Sehr unschön.

An dieser Stelle definiere ich die Exceptions meist als Teil der Schnittstelle (UserExistsException, EMailExistsException, InvalidException, etc). Das hat gerade bei EJB auch den sehr sinnvollen Vorteil das eine eventuell vorhandene Transaktion automatisch zurückgerollt wird... Ohne Exception gibts auch kein Rollback.

Mit ist noch kein eleganterer Weg eingefallen - man könnte noch ErrorCodes benutzen und diese Bestandteil der Schnittstellendefinition machen (-1 = Benutzer existiert, -2 = EMail existiert, etc). Das hätte auch Performancevorteile, damit zerstöre ich aber die Lesbarkeit meines Codes und muss die Transaktionen manuell steuern.

Und man kann es auch so sehen:
Für die Anwendung ist es kein aussergewöhnlicher Zustand, aber für die Schnittstelle und speziell für diese Methode ist es eine "Ausnahme" welche sie dazu zwingt die erfolgreiche Ausführung zu unterbrechen.


----------



## deamon (22. Jul 2008)

Gerade in verteilten, sehr lose gekoppelten Systemen sollte man aber nicht Exceptions an Schnittstellen verwenden. Sonst wäre ja der gesamte Code auf der anderen Seite der Schnittstelle vond en Exceptions abhängig. Eigentlich sollten da nur primitive Datentypen hin und her gereicht werden. 

Sehr lesenswert dazu ist übrigens das Buch "Moderne Softwarearchitektur" von Johannes Siedersleben.


----------



## ps (22. Jul 2008)

deamon hat gesagt.:
			
		

> Gerade in verteilten, sehr lose gekoppelten Systemen sollte man aber nicht Exceptions an Schnittstellen verwenden. Sonst wäre ja der gesamte Code auf der anderen Seite der Schnittstelle vond en Exceptions abhängig. Eigentlich sollten da nur primitive Datentypen hin und her gereicht werden.



Das verstehe ich nicht - natürlich ist die andere Seite von den Exceptions abhängig - das muss sie sein. Ob ich die Exception jetzt in Form von "-1" werfe, oder ob ich Java Exceptions benutze.... Den Performance Punkt könnte man anführen..

Natürlich geht das auch nur wenn man sich sicher sein kann das dies keine Schnittstelle ist welche von externen Systemen angesprochen werden muss (welche am Ende überhaupt nicht in Java geschrieben sein müssen). Ich rede hier aber eher von internen Schnittstellen (das Beispiel Benutzer erstellen passt da schön hinein). Es gibt aber dennoch mehr als einen Client der diese Funktion aufruft.



> Sehr lesenswert dazu ist übrigens das Buch "Moderne Softwarearchitektur" von Johannes Siedersleben.



Ich kenne das Buch - wirklich sehr lesenswert. Aufgrund der Aussagen über Exceptions hatte ich auch immer ein schlechtes Gewissen bei solchen Konstrukten  Mittlerweile bin ich aber der Meinung das dies in manchen Fällen durchaus aktzeptabel ist.


----------



## Neuer Gast (der Alte) (23. Jul 2008)

Hallo



> Das weitaus größere Problem stellt dar das in der Zeit zwischen meiner Prüfung (istNutzerHinzufuegbar) und dem Aufruf (nutzerHinzufuegen) sich die Kondition bereits geändert haben kann. zB. hat sich ein anderer Benutzer mit diesem Namen registriert.



Ist das nicht ein allgemeines Problem, wenn man die Transaktion NICHT auf Isolation.SERIALIZABLE setzt ?

Denn selbst waehrend der Pruefung kann sich der "Datenbestand" aendern und die "ValidierungsException" wird nicht geworfen...

Dann helfen nur noch Constraints auf der DB, die eine Exception ausloesen und dies ist dann in der Tat eine Ausnahmesituation...



> Die Anwendung schmeisst dann einen Fehler per "assert" und der aufrufende Code ist nicht in der Lage dies abzufangen. Sehr unschön.



In jeder Anwendung sollte es ein allgemeines ExceptionHandling der RuntimeExceptions geben. Diese greift dann natuerlich auch in diesem Fall.

Kleines Bsp zur Veranschaulichung meiner Haltung:

Wenn man aus einem Flugzeug springt sollte man vorher die Frage stellen, ob "istFallschirmVorhanden() : Response" und nicht nachdem man das Flugzeug verlassen hat eine "FallschirmNotFoundException" werfen. Das ist meist sehr ungesund...

ps: Falls jemand nach Aufruf von "istFallschirmVorhanden()" den Fallschirm vom Ruecken "abmontiert", dann ist in der Tat eine Ausnahmesituation eingetretten...


----------



## HLX (23. Jul 2008)

Neuer Gast (der Alte) hat gesagt.:
			
		

> Wenn man aus einem Flugzeug springt sollte man vorher die Frage stellen, ob "istFallschirmVorhanden() : Response" und nicht nachdem man das Flugzeug verlassen hat eine "FallschirmNotFoundException" werfen. Das ist meist sehr ungesund...


Findest du eine Validierung auf Reiseflughöhe nicht etwas verspätet? Erst ins Flugzeug steigen, abheben und dann schauen ob alles da ist?


----------

