In dem Beispiel fehlt noch die Behandlung welche Division fehlgeschlagen ist. Wie das da schön geht, erschließt sich mir nicht.
Indem in der Division-Methode ein Logging eingebaut wird. Viel hilft es zwar nicht, denn man kann ja nur loggen: 'Bei der Division von 1.2345/0' ist ein Fehler aufgetreten.
Bei Delphi ist es ja auch nicht soo einfach, den Grund einer Fehlrechnung zu erkennen:
Delphi-Quellcode:
Try
A:=B/C/D/E/F;
Except
On EDivideByZero do
// Soll ich hier C,D,E,F prüfen? Blödsinn
// Also, was tun?
...
Manchmal ist ein Null-Objekt sinnvoll, manchmal nicht. Ich hatte neulich den Fall, das eine Bitmap skaliert werden sollte, also:
Function ScaleBmp (aBitmap : TBitmap; Width : Integer) : TBitmap;
Was soll zurückkommen, wenn aBitmap=null? Fail-Fast sagt:
Exception! Ich habe gesagt: Na ja... entweder eine leere Bitmap (h=b=0)oder Nil. Wenn ich letzteres verwende, muss ich aber mein gesamtes Framework auf den Sonderfall 'nil' umstellen, ginge, wäre aber blöd.
Wenn ich aber eine leere Bitmap nehme, kann ich transparent mit allen weiteren Methoden arbeiten, ohne auf Nil prüfen zu müssen, was den Code doch etwas unleserlich machen würde. Ich kann eine leere Bitmap skalieren (kommt eh ne leere Bmp raus), filtern, weichzeichnen, Kantenerkennung betreiben, speichern, laden, usw. usw.
Wenn ich einen Rand um eine leere Bmp zeichne, dann entsteht allerdings (je nachdem, ob ich das will) eine nichtleere Bmp oder wieder eine leere Bmp.
Noch ein Wort zu Exceptions. Wir verwenden Exceptions auch zur Validierung:
Delphi-Quellcode:
Procedure TStorage.Store (theObject : TSomeStuff);
Begin
try
theObject.Validate();
Persist(theObject);
Except
On E:
Exception Do HandleError(E);
End
End;
Procedure TStorage.HandleError(E:
Exception);
Begin
If E
is EValidationException
then Raise;
If E
is EPersitingException
then begin
Log('
Probleme beim Speichern', E);
Raise ECannotStoreException.Create('
Speicherprobleme');
end;
if E
is Exception then begin
Log('
Programmierfehler', E);
Raise ECannotStoreException.Create('
Programmierfehler');
end;
end;
Alle Public-Methoden der TStorage-Klasse haben einen Try-Except-Wrapper, der HandleError aufruft. Alle Exceptions, mit Ausnahme der EValidationException, werden gekapselt.
Der Aufrufer muss dann nur die beiden Exceptions abfangen.
Bei einer Validation
Exception weiß er dann, das er ne Meldung anzeigen kann und den Anwender die Chance gibt, seine Daten zu ändern.
Ansonsten kann nur eine ECannotStoreException auftreten. Die kann er auch auswerten und z.B. den Vorgang beenden, denn diese
Exception bedeutet, das es (derzeit) nicht funktioniert. Konkret müsste man natürlich die genaue Fehlerursache ermitteln (indem der Persister z.B. geeignete Informationen bereitstellt).
Auf jeden Fall sieht der Validator immer gleich aus:
Delphi-Quellcode:
Procedure TSomeStuff.Validate;
Begin
If SomeData<23 then Raise EValidationException.Create('SomeData too small');
...
Man kann hier auch mit Rückgabewerten arbeiten, aber wenn der Validator wieder andere Validatoren aufruft, wird das wieder nervig. Mit Exceptions bleibt der Code immer einfach zu lesen.