Name eines Anti-Patterns gesucht



  • Hallo,

    ich habe letztens etwas über ein Anti-Pattern gelesen, in dem es darum ging, dass manche Programme eine Routine wie (Pseucode)

    wenn (datei_existiert(dateiname)) dann { lösche_datei(dateiname) }
    

    beinhalten. Zwischen der Prüfung auf die Existenz der Datei und dem Löschen könnte ja theoretisch das Betriebssystem die Datei löschen und es würde in manchen Sprachen in diesem Fall eine Exception fliegen. Besser wäre also z. B.:

    try
    {
        lösche_datei(dateiname)
    }
    catch (FileNotFoundException)
    {
        benutzerausgabe("Datei existiert nicht!")
    }
    

    Wie lautet der Name des Anti-Patterns zu ersterem?



  • 1.) Noch nie gehoert. 2.) Sieht sehr nach Java, C++, ... aus. Also sprachspezifisch.

    Sonst: Man koennte auch einfach loesche Datei aufrufen ohne auf Existenz zu pruefen. Anhand des Fehlercodes kann dann Fehlerbehandlung geschehen wie beispielsweise in C. Es gibt wesentlich mehr, was beim Loeschen schiefgehen kann, als fehlende Datein (was das harmloste ist, da die Nachbedingung sich nicht vom tatsaechlichem Loeschen unterscheidet).



  • Wusste gar nicht, dass Fehler neuerdings als Antipatterns gelten.



  • "Voodoo". Weil die Computergeister es vielleicht nicht gern haben, wenn man eine nicht existierende Datei löscht.

    "Cargo Cult". Alle machen das so, also mache ich es auch.

    "Nummer Sicher". Dem Ex-Kollegen aus der alten Firma ist da mal irgendwas abgestürzt, als er eine Datei löschen wollte, aber sich beim Namen vertippt hatte.



  • Das grundlegende Problem ist, dass hier eine "Race Condition" entsteht.



  • Ich weiss nicht ob das einen Namen hat.
    Ich weiss nur dass es dir Art von Dummfehler ist, die entsteht, wenn Evangelisten am Werk sind und ihnen die OCD wiedermal durchknallt.
    In diesem Fall die Art "es dürfen im Normalbetrieb keine Exceptions fliegen" Evangelisten.



  • OCD?

    Wem gehört die Datei? Mir vielleicht? Bin gar nicht mal so sicher, daß es immer ein Fehler ist, durchzuknallen, wenn jemand sich an meinen Dateien zu schaffen macht.



  • volkard schrieb:

    OCD?

    Obsessive Compulsive Disorder

    Ich glaub aber hustbaer ist auf dem falschen Dampfer, mit Exceptions hat das Problem überhaupt nichts zu tun. Man könnte das genauso mit Fehlercodes formulieren. Der Punkt ist, dass Checken -> Tun eine Race Condition enthält, im Gegensatz zu Tun -> Erfolg Checken. Mal davon abgesehen, dass die bloße Existenz einer Datei noch kein erfolgreiches Löschen ermöglicht.



  • hustbaer schrieb:

    In diesem Fall die Art "es dürfen im Normalbetrieb keine Exceptions fliegen" Evangelisten.

    Das kann man doch gar nicht sagen ohne den Kontext des CodeSnippets zu kennen.



  • Bashar schrieb:

    Ich glaub aber hustbaer ist auf dem falschen Dampfer, mit Exceptions hat das Problem überhaupt nichts zu tun.

    Es hat insofern etwas damit zu tun, als es Leute gibt die

    * den Standpunkt vertreten dass in einem Programm "im Normalfall" keine Exceptions fliegen dürfen
    * argumentieren dass man "'geht nicht' kann normal sein" Fälle ja so implementieren kann, dass man vorher erstmal prüft ob's denn gehen wird oder nicht, und dann erst macht (damit beim "Machen" dann eben keine Exception fliegt)
    * übersehen dass es eben Fällt gibt, wo das nicht sinnvoll ist oder wie in diesem Fall nichtmal möglich (eben wegen besagter Race-Condition und anderen Problemen)
    und daher dann
    * Genau solchen "if (can_do) do()" Code schreiben wie ihn der OP gezeigt hat.

    Glaube mir, ich hab solche Diskussionen schon zur Genüge erlebt.
    Es treiben sich sogar hier im Forum welche rum die meinen dass man das so machen müsste (zum Glück weiss ich keine Namen mehr, und muss mich daher auch nicht zurückhalten sie jetzt zu nennen :D).

    Und du kannst statt "Exceptions" natürlich auch gerne eine andere Art der Fehlerbehandlung substituieren. Ob eine Exception fliegt oder "nur" eine Fehlermeldung in ein Logfile geschrieben wird ist dann schon fast egal.

    Und natürlich ist "die" richtige Lösung hier "TryXxx" Funktionen zu verwenden statt "Xxx" Funktionen die dann ne Exception wefen. Und wenn's nicht anders geht, dann implementiert man die "TryXxx" funktion halt als

    TryXxx()
        try
            Xxx()
            return true
        catch ExpectedError1
            return false
        catch ExpectedError2
            return false
    


  • Morle schrieb:

    hustbaer schrieb:

    In diesem Fall die Art "es dürfen im Normalbetrieb keine Exceptions fliegen" Evangelisten.

    Das kann man doch gar nicht sagen ohne den Kontext des CodeSnippets zu kennen.

    OK, ungenau ausgedrückt meinerseits.

    In diesem FallDer gezeigte Code ist etwas, was oft entsteht, wenn die Art "es dürfen im Normalbetrieb keine Exceptions fliegen" Evangelisten am Werk ist.

    Natürlich heisst das nicht dass der konkrete Code, den der OP vielleicht im Sinn hat, so entstanden sein MUSS.



  • hustbaer schrieb:

    In diesem FallDer gezeigte Code ist etwas, was oft entsteht, wenn die Art "es dürfen im Normalbetrieb keine Exceptions fliegen" Evangelisten am Werk ist.

    Nein, der gezeigte Code entsteht, wenn Bibliotheken mit der Einstellung "je mehr Exceptions wir um uns werfen, desto besser" geschrieben werden. Um solche Bibliotheken komfortabel benutzen zu können, muss man natürlich auch mit Exceptions um sich werfen.

    TryXxx()
        try
            Xxx()
            return true
        catch ExpectedError1
            return false
        catch ExpectedError2
            return false
    

    Man in keinem Fall die TryXXX-Funktionen mittels XXX implementieren, sondern umgekehrt, weil Exceptions langsam sind.



  • @normalfall
    Komm mal wieder runter.
    Du willst dich bloss aufregen, und nicht verstehen was ich schreibe.
    Wir meinen nämlich glaub ich im Prinzip das selbe. Ich bin auch für TryXxx Funktionen, und finde es ärgerlich, wenn in einer Library eine Funktion die es wirklich als Try-Variante geben sollte nur als Exception-Variante gibt.

    Zu deiner "Kritik" meines Beitrags...

    Was ist an "wenn's nicht anders geht" nicht zu verstehen?
    Ich meine Fälle, wo man eine fertige Xxx Funktion einsetzt, von der es keine TryXxx Variante gibt.

    Wenn die TryXxx Variante in 5 Zeilen selbst implementiert ist, OK, macht man das halt. Aber dann "geht es ja anders".

    Nur was machst du wenn du eine 10/100/1000 KLOC Library neu schreiben müsstest um die TryXxx Funktion ohne "catch" zu bekommen?
    Beruf wechseln?
    🙄

    normalfall schrieb:

    Man in keinem Fall die TryXXX-Funktionen mittels XXX implementieren, sondern umgekehrt, weil Exceptions langsam sind.

    FUD



  • hustbaer schrieb:

    * den Standpunkt vertreten dass in einem Programm "im Normalfall" keine Exceptions fliegen dürfen

    Es macht Sinn zu definieren, wann Exceptions fliegen sollen. Für mich fliegen im Normalfall auch keine Exceptions, aber ultimativ kommt es natürlich drauf an was man als Normalfall definiert. Die Definition über meine "Normalfälle" hilft mir im Programm Exceptions konsistent zu verwenden. Bei der Definition beziehe ich den Kontext/Verhalten mit ein:

    1. soll z.B. eine Datei auf /tmp gelöscht werden, würde ich es ohne Exceptions programmieren (oder wenn keine passende Funktion existiert die Exception lokal verarbeiten). Ich erwarte nämlich, dass ein frisch gestartetes OS mir ein leeres /tmp präsentiert und dann solche Fälle eben vorkommen können.
    2. soll die Routine eine Datei löschen, die ein Benutzer z.B. zuvor in einem "Datei-Löschen" Dialog ausgewählt hat, würde ich es mit Exceptions programmieren, weil ich höchst irritierend finden würde, wenn die Datei nach dem Schließen des Dialogs und eigentlichem Löschen auf einmal verschwunden wäre.

    Das heißt nicht, dass ich in Fall 1. auf try/catch verzichten würde, denn es könnten ja noch andere Dinge in diesem Fall schief gehen, die Exceptions wert sind: z.B. Löschen nicht möglich, weil die Datei von jemand anderem gelockt ist, etc.

    Was nämlich passiert, wenn man keine klare Definition verwendet zeigt folgendes Negativ-Beispiel: Bei uns parste jemand (nicht ich) ungeprüft Benutzereingaben und versuchte diese in Float-Werte mit einer Funktion die Exceptions werfen konnte zu konvertieren. Zu allem Übel standen diese Daten auch noch in Dateien. Das Konvertieren fand also bei jedem Programmstart statt und bewirkte, dass man sich bei inkorrekten Daten erstmal durch ca 50 Debuggerunterbrechnungen aufgrund Exceptions klicken durfte. Und man (ich) durfte damals oft im Debugger Dinge simulieren und komische Fehler suchen.
    Eigentlich war sogar dieses Erlebnis der Auslöser, der zur meiner aktuellen Defintion geführt hat.



  • Morle schrieb:

    hustbaer schrieb:

    * den Standpunkt vertreten dass in einem Programm "im Normalfall" keine Exceptions fliegen dürfen

    Das heißt nicht, dass ich in Fall 1. auf try/catch verzichten würde, denn es könnten ja noch andere Dinge in diesem Fall schief gehen, die Exceptions wert sind: z.B. Löschen nicht möglich, weil die Datei von jemand anderem gelockt ist, etc.

    Also ich meine damit ein weiter oben im Callstack angesiedeltes try/catch. Auf keinen Fall so wie im Beispiel. 😉



  • Lies vielleicht nochmal etwas weiter zurück. Darum geht's hier gar nicht.

    Es geht darum, dass if (can_do()) do() Blödsinn ist, wenn man can_do() nicht korrekt implementieren kann (bzw. es halt einfach nicht macht) ODER wenn sich die Bedingung "can do?" jederzeit ändern kann.



  • @hustbaer: ich versteh dich voll und ganz, aber hier im Forum gibt es immer wieder Leute, die einen falsch verstehen (oder verstehen wollen) - und daher enthalte ich mich diesen (sinnlosen) Diskussionen (auch wenn es viel Bedarf für Aufklärung gibt...).



  • hustbaer schrieb:

    Lies vielleicht nochmal etwas weiter zurück. Darum geht's hier gar nicht.

    Es geht darum, dass if (can_do()) do() Blödsinn ist, wenn man can_do() nicht korrekt implementieren kann (bzw. es halt einfach nicht macht) ODER wenn sich die Bedingung "can do?" jederzeit ändern kann.

    Das hatten wird doch schon geklärt und waren uns auch alle einig?!.

    Hatte auch gehadert ein Follow-Up über Exceptions zu posten, aber konnte deine Aussage dann leider doch nicht so stehen lassen 😞



  • Als Antipattern wuerde ich es nicht bezeichenen. Es gibt einige Anwendungsfaelle, beispielsweise:

    bool get_number(string_type str, double& d)
    {
        if (check_number(str)) //eg. with a regular expression or is digit or ...
           d = retrieve_number(str);
        else
          return false;
        return true;
    }
    

    Manche wuerden ja boost::lexical_cast benutzen. Ich wahrscheinlich nicht. Weils nur 'ne Zahl ist und weil ich vielleicht in C programmiere.



  • @knivil
    Vermutlich wäre es auch da billiger während des Parsens zu checken ob das Format OK ist.
    Weil ein robuster Parser das sowieso macht.
    Ein zweiter Check davor ist dann sinnlos.

    Falls man die check_number Funktion trotzdem braucht, kann diese einfach den Parser aufrufen, und den Wert in eine Dummy-Variable reinparsen lassen.


Log in to reply