Callbacks und Listener. Kennt wer gute Beispiele?



  • Hallo,
    nachdem ich mit der Pointer-Arithmetik sehr vertraut geworden bin, wollte ich mich mit dem Gebrauch von Callbacks und Listeners auseinander setzen. Problem ist nur, dass ich keine guten, bzw. nur Proof-Of-Concept-Beispiele finde.
    Mir ist unklar, wann man von Listener und Callbacks Gebrauch machen sollte gegenüber von einfachen Funktionsaufrufen .
    Kennt jemand gute Beispiele?
    danke



  • Stichwort: lose Kopplung

    Also immer dann, wenn der Aufrufer der Funktion nichts von der Funktion selbst kennen soll (also erst recht nicht Namen und Herkunft).
    Beispiele:

    • Implementierung eines Buttons in einem Framework - nur die Anwendung weiß, was beim Button-Klick konkret passieren soll (und je nach Button soll ja eine andere Funktion aufgerufen werden).
    • Iterieren einer Liste oder eines Baumes: Ausgabe oder Bearbeitung mittels Listener (s.a. Beobachter (Entwurfsmuster))

    Beispiele von Implementierungen davon gibt es z.B. bei



    • Jede GUI arbeit so. Du hast einen Button, der beim Klick einen Handler aufruft. Oder eine Liste von Handlern.
    • bei asynchroner I/O. An irgendeiner Hardware-Schnittstelle kommen Daten an und das Objekt, das die Kommunikation abwickelt, benachrichtig über einen Callback den Listener.
    • generisch: Wenn in deinem Programm irgendwo etwas passiert möchtest du darüber benachrichtigt werden, allerdings möchtest du die Änderungen nicht fest verdrahten, sondern über ein Publisher/Subscriber Interface anbieten. Bsp: Du lässt eine Datenbankabfrage laufen, deren Ergebnis unterschiedlich visualisiert wird. Statt jetzt jedes Formular, das die Daten anzeigt, per Funktionsaufruf zu aktualisieren registrieren sich die Formulare als Subscriber an der Datenquelle und aktualisieren sich durch das Auslösen des Publishers.

    Generell ist der Vorteil dieses Modells, dass sich zwei Komponenten (Publisher & Subscriber) nicht kennen müssen, sondern nur über ein Interface interagieren. Der Nachteil ist, dass der Programmablauf nicht mehr so einfach zu verfolgen ist, weil nicht ersichtlich ist, welche Subscriber registriert sind und was das Aufrufen des Subscribers bewirkt.

    // klassisch
    
    #include "Form1.h"
    #include "Form2.h"
    #include "Form3.h"
    #include "DataSource.h"
    
    int main()
    {
       ...
       while( !terminated )
       {
          auto some_data = Source.data();
          if( Form1 ) Form1->update( some_data );
          if( Form2 ) Form2->update( some_data );
          if( Form3 ) Form3->update( some_data );
       }
    }
    

    Die Formulare Form1, Form2 und Form3 sollen unterschiedliche Ansichten auf die Daten sein, die vom Benutzer beliebig ein- und ausgeblendet werden können. Daher muss man hier prüfen, ob die Formulare gerade existieren oder nicht, und bei weiteren Formularen müssen die auch an dieser Stelle bekannt gemacht werden. Das führt zu einen engen Abhängigkeit von Datenquelle und Formularen.

    // Publisher & Subscriber (ohne RAII, der Einfachheit halber)
    // Form1.h
    
    #include "DataSource.h"
    class Form1
    {
        ...
       Subscriber OnChange_;
    
       Form1()
       {
          // als Subscriber beim Publisher registrieren
          OnChange_ = SomeDataSource.OnChange.connect( this, &on_data_changed );
       }
    
       ~Form1()
       {
          // vom Publisher abmelden
          OnChange_.disconnect():
       }
    
       void on_data_changed( ... )
       {
          // Behandle Daten
       }
    }
    
    
    // Klasse DataSource.h
    struct DataSource
    {
       Publisher OnChange;
    
       void query()
       {
          auto some_data = query_data();
    
          // Der Datenquelle ist hier völlig egal, wer oder was sich als Sink eingetragen hat
          OnChange.fire( some_data );
       }
    }
    

    In Qt ist das Konzept als signal/slot umgesetzt, in den boost Bibliotheken gibt es die Bibliothek signals2.


  • Mod

    Das brauchst du immer dann, wenn du zu der Zeit, wo du den Code schreibst, der die Funktion aufrufen soll, nicht weißt, welche Funktion er aufrufen soll. Ich weiß nicht, was du dir noch für Beispiele erhoffst, neben den Standardbeispielen. Du hast doch die Standardbeispiele angeguckt, oder? Wenn du ein Programm mit einem grafischen Button hast, wo irgendetwas passieren soll, wenn der Knopf gedrückt wird, dann kann der Code von dem Knopf schließlich noch nicht die Funktion enthalten, die er aufrufen soll. Den Code für den Button hat irgendwann mal vor vielen Jahren jemand geschrieben, der das grafikframework programmiert hat. Da gab es die Funktion, die der button aufrufen soll, noch gar nicht.



  • Danke soweit 🙂
    ich denke, damit kann ich schon mal was anfangen


  • Mod

    @sucy_manbavaran sagte in Callbacks und Listener. Kennt wer gute Beispiele?:

    Danke soweit 🙂
    ich denke, damit kann ich schon mal was anfangen

    Ich glaube, die beste Methode, zu kapieren, wozu das gut ist, ist einfach irgendetwas zu programmieren, wo man es braucht. Dann wird das ganz von selber klar. Programmier irgendetwas mit einer grafischen Oberfläche. Oder programmier irgendetwas in OpenGL/DirectX. Bei allem wirst du massiv Callbacks brauchen, und es ist ziemlich offensichtlich, wieso das keine direkten Funktionsaufrufe sein können. Du solltest dich nur nicht von der abstrakten Informatikersprache abschrecken lassen. Die Konzepte dahinter sind ganz einfach.


Log in to reply