Name eines Anti-Patterns gesucht



  • 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.



  • Morle schrieb:

    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 😞

    Ich versteh nicht was dir nicht passt. Anhand der Formulierung deines Beitrages ist klar, dass du mir widersprichst, bzw. mich sogar belehren willst.
    Nur sachlich steht da nichts was meiner Aussage widerspricht.
    z.B. hab ich nie behauptet dass man sich keine Gedanken darüber machen sollte wann Exceptions fliegen dürfen.

    Bzw. schreibst du sogar ein paar Dinge die mMn. sogar höchst fragwürdig sind. Ich verstehe z.B. nicht warum es schlimm ist, wenn bei fehlerhaften Input-Files irgendwo ne Exception fliegt. Ich meine, dafür sind Exceptions ja schliesslich da - um Ausnahmefälle, wie fehlerhafte Input-Files, zu behandeln. Für Dinge die überhaupt niemals nicht passieren dürfen kann man Assertions verwenden (die man ja ohne weiteres auch im Release-Build mit drinlassen kann).



  • Ich meine, dafür sind Exceptions ja schliesslich da - um Ausnahmefälle, wie fehlerhafte Input-Files, zu behandeln.

    Ich dachte, Exceptions wären dafür da, Fehler, die nicht erwartet sind zu behandeln.

    Gehört denn ein fehlerhafte Datei (die vom User gegeben ist) i.A. dazu? Belehre mich, Meister!


  • Mod

    Arcoth schrieb:

    Ich meine, dafür sind Exceptions ja schliesslich da - um Ausnahmefälle, wie fehlerhafte Input-Files, zu behandeln.

    Ich dachte, Exceptions wären dafür da, Fehler, die nicht erwartet sind zu behandeln.

    Gehört denn ein fehlerhafte Datei (die vom User gegeben ist) i.A. dazu? Belehre mich, Meister!

    Natürlich ist das eine Ausnahme. Eine Datei ist eine formatierte Form der Eingabe und wenn das Format nicht passt, dann ist das eine Ausnahme.



  • Um mal den Erstsemester Talk hier zu unterbrechen und die Frage zu beantworten:
    das "Pattern", nennt sich "Easier to ask forgiveness than permission" (EAFP) bzw. "Loop before you leap" (LBYL). Keines von beiden ist ein "Anti-Pattern", beide sind halt gegensätzliche Idiome in verschiedenen Sprachen, wobei gerade für Dateisystemoperationen wie im Beispiel oder in multithreaded anwendungen EAFP viel Sinn macht, egal welche Sprache du benutzt.



  • @Arcoth
    Der Knackpunkt ist: was bedeutet "nicht erwartet"?
    Überleg dir einfach mal ein Beispiel: eine Funktion

    unique_ptr<Image> ExtractFrame(string const& videoFilePath, size_t frameNumber);

    Eine höchst unvollständige Liste von Dingen die dabei schief gehen können:
    * Das File existiert nicht
    * Das File existiert, kann aber nicht gelesen werden (z.B. weil von nem anderen Prozess mit "deny read" geöffnet)
    * Das File existiert, hat aber keines der unterstützten Container-Formate
    * Das File existiert, hat auch ein unterstütztes Container-Format, aber keinen Video-Stream
    * Das File existiert, hat auch ein unterstütztes Container-Format, aber der Video-Stream hat ein nicht unterstütztes Format
    * Das File existiert, ..., hat aber weniger Frames als frameNumber
    * Das File existiert, ..., aber irgendwo in dem ganzen Prozess ist der Speicher ausgegangen
    * Das File existiert, hat auch grundsätzliche ein gültiges Format, aber die Daten für Frame # frameNumber sind defekt (Prüfsummenfehler oder was weiss ich)
    uswusf.

    Jetzt kann man argumentieren: das sind alles Dinge die die Funktion erwarten muss, also alles "Normalfall", also nirgends Exceptions verwenden. Diese Argumentation halte ich für beknackt. Denn dann können wir Exceptions gleich einäschern -- es bleibt dann nämlich nichts mehr übrig wo wir wirklich Exceptions verwenden sollten. Ausser als Ersatz für Assertions. Nur dafür sind Exceptions eigentlich gar nicht so gut geeignet, denn wenn eine Assertion danebengeht, dann ist fraglich ob man das Programm überhaupt noch fortsetzen sollte.

    Man kann aber auch argumentieren: Es gibt hier genau einen "erwarteten" Fall, und zwar dass alles passt und die Funktion erfolgreich ein Frame extrahieren kann. Alles andere sind dann Ausnahmen, und wir verwenden dafür Exceptions. Diese Argumentation halte ich für viel sinnvoller.

    Je nachdem für was die Funktion genau eingesetzt wird können vielleicht noch weitere "normale" Fälle dazukommen. Das würde aber etwas zu weit führen, hier geht's ja nur um ein Beispiel.

    BTW: Theoretisch kann man Exceptions auch für beides verwenden, und die Fälle sogar unterscheiden. Die "Assertion-artigen" Fehler wären dann von std::logic_error abgeleitet, und alle anderen von std::runtime_error . Dummerweise gibt's aber viel zu viele Libraries die eigene Exception-Klassen verwenden, die entweder direkt von std::exception abgeleitet sind, oder gleich überhaupt gar keine Basisklasse haben. Dadurch ist es in der Praxis nicht möglich zu sagen "fang alle runtime Fehler" -- es sei denn man verwendet keine fremden C++ Libraries, und hat keinen eigenen Legacy-Code der einem dreinpfuschen würde.

    EDIT: Grammatik.



  • huhasfuhduhuhu schrieb:

    Um mal den Erstsemester Talk hier zu unterbrechen

    Oh grosser grosser Meister, belehre uns!!! 🙄

    huhasfuhduhuhu schrieb:

    "Loop before you leap" (LBYL).

    Look before you leap.
    Ich kannte den Begriff nicht, aber dass "Loop before you leap" (offenbar ein Markenname für irgendwelche Damenschuhe) Quatsch ist war irgendwie offensichtlich.



  • unique_ptr<Image> ExtractFrame(string const& videoFilePath, size_t frameNumber);
    

    Das Problem mit Funktionen im God Mode ist ... der God Mode. Ich sach nur MMMMMMMonster kill.

    Ansonsten kann das Beispiel auch sehr gut mit Fehlercodes funktionieren. Ob irgendwo ein if (...) throw ... oder if (res != SUCCESS) return ... ist dabei unerheblich. Manchmal nutze ich auch:

    bool fun1(...);
    bool fun2(...);
    
    bool b = true;
    b = b && fun1...
    b = b && fun2...
    return b;
    

    Der Unterschied ist semantischer Natur.



  • knivil schrieb:

    unique_ptr<Image> ExtractFrame(string const& videoFilePath, size_t frameNumber);
    

    Das Problem mit Funktionen im God Mode ist ... der God Mode. Ich sach nur MMMMMMMonster kill.

    Was für ein sinnfreies Kommentar!

    Dir ist schon klar dass ich in keiner Weise auch nur angedeutet habe, dass diese Funktion als Monolith implementiert sein soll, ja?
    Aber nö, ist dir vermutlich nicht klar, weil du ja immer davon ausgehst dass was andere schreiben nur Mist sein kann.

    Alter Schwede!



  • Was für ein sinnfreies Kommentar!

    Mich zu verstehen ist schwer. Der Fehler liegt bei mir.

    Monolith implementiert sein soll

    Wie sie implementiert ist, ist doch egal. Die Funktion ist ein schlechtes Beispiel, da sie zu viel macht. Deswegen halte ich das Beispiel fuer ungeeignet.

    weil du ja immer davon ausgehst dass was andere schreiben nur Mist sein kann

    Unterstellung. Aber vielleicht hast du recht: ninety percent of everything is crap


Anmelden zum Antworten