![]() |
AW: Best Practice: Wann verwendet ihr Exceptions in Funktionen?
Zitat:
Zitat:
Denk mal drüber nach. Und wenn Du schon dabei bist (am Nachdenken), überlege Dir auch noch, ob dich überhaupt jemand kritisiert hat und wieso Du hier so ausrastest. Ich wollte Dir jedenfalls nicht an den Karren pissen. Zitat:
Das Handle-Beispiel könnte man auch so lösen, das die 'GetHandleFromElsewhere'-Methode die Exception-Behandlung übernimmt. Oder man macht soetwas: LHandle1 := CheckHandle (GetHandleFromElseWhere, 'From ElseWhere'); LHandle2 := CheckHandle (GetHandleFromElseWhere (LHandle1), 'From ElseWhere 1'); LHandle3 := CheckHandle (GetHandleFromElseWhere (LHandle2), 'From ElseWhere 2'); LHandle4 := CheckHandle (GetHandleFromElseWhere (LHandle3), 'From ElseWhere 3'); ... Wobei das CheckHandle eine Funktion ist:
Delphi-Quellcode:
Macht Delphi z.T. ja auch so.
Function CheckHandle (aHandle : THandle; Msg : String) : THandle;
Begin if aHandle = InvalidHandle then Raise Exception.Create(Msg); result := aHandle; End; Ich persönlich finde das aber nicht sooo lesbar. Es ist zu empfehlen, wenn man die GetXXXX-Funktionen nicht ändern kann. Man kann die aber Kapseln, sodaß die Exception-Behandlung im Wrapper stattfindet. Solch ein Code (3-Zeiler) kann ruhig komisch aussehen, da schaut man kurz rein, sagt:'Ach so' und macht das Zeugs wieder zu. Die Wrapper sind dann aber sehr gut zu lesen:
Delphi-Quellcode:
Und das ist dann schon wieder schön knackig.
Try
LHandle1 := GetHandle; LHandle2 := GetHandle(LHandle1); LHandle3 := GetHandle(LHandle2); LHandle4 := GetHandle(LHandle3); DoSomething(LHandle1,LHandle2,LHandle3,LHandle4); Except On E:EInvalidHandle Do begin LogError(E); Raise EMoreAbstractException.Create(E); End; End; Die 'EMoreAbstractException' analysiert die innere Exception (kann ja unterschiedliche Gründe haben) und erzeugt eine Exception, die die Details verschweigt. Der Aufrufer interessiert sich ja nicht dafür, das der USB-Treiber aufgrund ungültiger Installationsparameter im 3.Duktus links kein Handle bereitstellen kann. Wichtig ist: 'Es funzt net'. Details stehen im Log. @JasonDX: Wenn da wirklich nur ein Return erfolgen soll, ist das grenzwertig. Meist ist so ein stillschweigendes Scheitern schlechter Stil. WENN es aber genaus so sein soll, ist das mit dem Return schon so richtig. Da ginge aber auch (besser?) sowas:
Delphi-Quellcode:
Wieder knackig kompakt :stupid:
if not TryGetHandle (LHandle1) then return;
if not TryGetHandle (LHandle1,LHandle2) then return; if not TryGetHandle (LHandle2,LHandle3) then return; if not TryGetHandle (LHandle3,LHandle4) then return; |
AW: Best Practice: Wann verwendet ihr Exceptions in Funktionen?
Zitat:
![]() Exceptions sind mir persönlich einfach zu low level :mrgreen: Ich meine, wozu denkt man sich von der technischen Ebene völlig abstrahierte Konzepte wie OOP aus, wenn man dann doch wieder auf etwas zurückgreift, was doch sehr von der Hardware-Ebene inspieriert anmutet (Interrupts...). Edit: ein anderes Beispiel wäre das ![]() |
AW: Best Practice: Wann verwendet ihr Exceptions in Funktionen?
Zitat:
Zitat:
Ich denke kein Stück an Interrupts wenn ich eine Exception schmeiße, sondern daran, das gerade ein besonderer Fall in meinem Code aufgetreten ist, den ich in meiner derzeitigen Methode nicht behandeln kann. Zitat:
Code:
Wenn ich mit Exceptions arbeite weiß ich, dass wenn ich bspw.
parseUrl(String): Url
Code:
aufrufe und keine Exception geworfen wird (und myUrl somit als zugewiesen gilt), ich in myUrl eine wunderschöne Url-Instanz habe. Wenn ich hingegen eine Try-Klasse oder Fehlermeldungen durch Rückgabewerte oder CallByReference-Parameter verwende, was habe ich dann in myUrl? Vielleicht ne Url, vielleicht aber auch nicht.
myUrl = parseUrl(input);
|
AW: Best Practice: Wann verwendet ihr Exceptions in Funktionen?
Zitat:
|
AW: Best Practice: Wann verwendet ihr Exceptions in Funktionen?
Zitat:
Denn ständig zu prüfen, ob es das Objekt gibt oder nicht um, wenn ja, es dann zu benutzen ist dann etwas umständlich. Vor allem, wenn dann dieses Objekt auch noch von aussen angesprochen werden soll.
Delphi-Quellcode:
Als Logger kann ich jetzt also irgendwas dort unterjubeln, allerdings kann ich zum Ausschalten des Loggens einfach ein
ILogger = interface
procedure Log( const Text : string ); end; TFoo = class private FLogger : ILogger; function GetLogger : ILogger; procedure SetLogger( const Value : ILogger ); public property Logger : ILogger read Getlogger write SetLogger; procedure Bar; end; implementation type TNullLogger = class( TInterfacedObject, ILogger ) procedure Log( const Text : string ); end; procedure TNullLogger.Log( const Text : string ); begin // do nothing end; procedure TFoo.SetLogger( const Value : ILogger ); begin FLogger := Value; end; function TFoo.GetLogger : Ilogger; begin if not Assigned( Flogger ) then FLogger := TNullLogger.Create; Result := Flogger; end; procedure TFoo.Bar; begin Logger.Log( 'Bar from TFoo Start' ); Logger.Log( 'Bar from TFoo Step 1' ); Logger.Log( 'Bar from TFoo Step 2' ); Logger.Log( 'Bar from TFoo Step 3' ); Logger.Log( 'Bar from TFoo Stop' ); end; end.
Delphi-Quellcode:
übergeben, und trotzdem knallt es nicht, es wird aber auch nichts geloggt.
nil
|
AW: Best Practice: Wann verwendet ihr Exceptions in Funktionen?
Zitat:
Code:
Die Überprüfung, ob die ganze Geschichte erfolgreich war, musst du genau ein mal machen, nämlich zum Schluss, genau dann, wenn du das Endergebnis brauchst. Da man mehrere Trys auf diese Weise problemlos verketten kann, sparst du dir da SEHR VIEL Fehlerbehandlungscode.
maybeInt.map(x => x * 2)
Zitat:
Zitat:
Zitat:
Code:
interface IComputation {
function currentValue: Float; function multiplyBy(other: Float): IComputation; function divideBy(other: Float): IComputation; } class Computation extends IComputation { function divideBy(other: Float): IComputation { if (other = 0) return new NullComputation(); return new Computation(this.currentValue / other); } ... } class NullComputation extends IComputation { function currentValue: Float { return Float.NaN } function divideBy(other: Float): IComputation { return this; } ... } new Computation(5).divideBy(3).divideBy(0).divideBy(5).currentValue => NaN |
AW: Best Practice: Wann verwendet ihr Exceptions in Funktionen?
Zitat:
|
AW: Best Practice: Wann verwendet ihr Exceptions in Funktionen?
Zitat:
Bei Delphi ist es ja auch nicht soo einfach, den Grund einer Fehlrechnung zu erkennen:
Delphi-Quellcode:
Manchmal ist ein Null-Objekt sinnvoll, manchmal nicht. Ich hatte neulich den Fall, das eine Bitmap skaliert werden sollte, also:
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? ...
Delphi-Quellcode:
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.
Function ScaleBmp (aBitmap : TBitmap; Width : Integer) : TBitmap;
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:
Alle Public-Methoden der TStorage-Klasse haben einen Try-Except-Wrapper, der HandleError aufruft. Alle Exceptions, mit Ausnahme der EValidationException, werden gekapselt.
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; 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:
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.
Procedure TSomeStuff.Validate;
Begin If SomeData<23 then Raise EValidationException.Create('SomeData too small'); ... |
AW: Best Practice: Wann verwendet ihr Exceptions in Funktionen?
Zitat:
|
AW: Best Practice: Wann verwendet ihr Exceptions in Funktionen?
Zitat:
Sowas (= konkret eine Fehlbedienung) sollte maximal beim Entwickeln passieren und nicht im normalen Programmablauf. Und wenn doch, ist woanders was schief gelaufen (Ausnahmesituation) ;-) Da sind Exceptions natürlich das Mittel der Wahl. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 07:14 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz