Rückruffunktion

aus Wikipedia, der freien Enzyklopädie
Wechseln zu: Navigation, Suche

Eine Rückruffunktion (englisch callback function) bezeichnet in der Informatik eine Funktion, die einer anderen Funktion als Parameter übergeben und von dieser unter gewissen Bedingungen aufgerufen wird. Dieses Vorgehen folgt dem Entwurfsmuster der Inversion of Control.

Rückruf im selben Ablauf[Bearbeiten]

ProgramExecCallbackSimple-de.png

Das Bild zeigt einen Aufruf einer Routine eines anderen Kontextes (Betriebssystemaufruf, anderes Softwaremodul). Diesem Aufruf wird eine Referenz auf die Rückrufroutine übergeben. Die aufgerufene Routine kann dann in ihrem Ablauf die Rückrufroutine aufrufen. Die Rückrufroutine legt letztlich Daten ab, die im weiteren Programmablauf nach Rückkehr des ursprünglichen Aufrufes verwendet werden können. In der englischen Literatur wird diese Form als blocking callback oder synchronous callback bezeichnet.

Der Vorteil dieser Technik besteht darin, dass der Zugriff auf die Daten im eigenen Kontext formuliert wird und/oder im Rückruf eine beliebige programmtechnische Aufbereitung erfolgen kann: Die Parameter und der Aufrufzeitpunkt werden im anderen Softwaremodul bestimmt, die Bestimmung was im Rückruf ausgeführt wird obliegt dem eigenen Softwaremodul.

Dadurch wird ermöglicht, Funktionen allgemein zu definieren und erst beim Aufrufen der Funktion durch Angabe der Rückruffunktion das Verhalten exakt zu bestimmen. Häufig bekommt eine Rückruffunktion gar keinen Namen, sondern wird als anonyme Funktion direkt beim Aufruf definiert (siehe auch Lambda-Kalkül). Über Rückruffunktionen erreicht man eine lose Kopplung zwischen einzelnen Komponenten.

Rückruf aus einem anderen Thread[Bearbeiten]

Callback mit Thread-Interkommunikation.png

Bei Angabe einer Rückruffunktion ist es der aufgerufenen Funktion überlassen, wann diese den Rückruf ausführt (Inversion of Control). Es ist möglich, dass der Rückruf zu einem späteren Zeitpunkt aus einem anderen Thread heraus erfolgt. Das nebenstehende Bild zeigt das Prinzip. Die Zuordnung der Rückrufroutine zum eigenen Kontext ist die Gleiche wie im obigen Schema. Da der Rückruf in einem anderen Thread erfolgen kann, sind aber zusätzlich die Bedingungen für eine Datenkonsistenz (Mutex) einzuhalten. In der englischen Literatur wird diese Form als deferred callback oder asynchronous callback bezeichnet.

Ein Beispiel dafür ist der Aufruf SetTimer(hWnd, ident, milliseconds, callbackRoutine) in der Windows-API. Der Aufruf SetTimer(...) ist ein kurzer Betriebssystemaufruf, der nur im Betriebssystem den Timer initialisiert. Die callbackRoutine wird erst dann aufgerufen, wenn die Zeit abgelaufen ist. Das geschieht in einem Thread des Betriebssystems unabhängig vom Ablauf nach dem Aufruf von SetTimer(...). In der bereitzustellenden Rückrufroutine kann beliebiger Code ausgeführt werden. Es ist allerdings dabei zu beachten, dass die Rückrufroutine nur kurz und nicht blockierend sein darf. Sonst besteht die Gefahr, dass das gesamte Betriebssystem oder mindestens der aktuelle Prozess blockiert. Letztlich sollte die Rückrufroutine eigentlich nur Daten ablegen und nicht selbst komplexe Aktionen beinhalten. Insbesondere kann für eine ereignisorientierte Programmierung eine Event-Instanz erzeugt und abgelegt werden, die weitere Abläufe steuert.

Dauerhafte Rückrufroutine[Bearbeiten]

Eine Rückrufroutine kann nur jeweils für einen Aufruf gelten. Die Rückrufroutine kann in Bezug zum Aufruf auch mehrmals gerufen werden, bis die zugehörige Aktion beendet ist, also zeitlich begrenzt.

Aber auch die dauerhafte Zuordnung einer Routine wird teilweise als callback routine bezeichnet, siehe beispielsweise msdn.microsoft... ThreadProc callback function: Bei der Anlage eines Threads über die Windows-API wird die Threadfunktion als callback function angegeben.

Die Zuordnung einer Ereignisbehandlungsroutine beispielsweise zu einem HTML-Element <button onclick="function()" /> oder zu einem Widget innerhalb einer Programmierung grafischer Benutzeroberflächen folgt demselben Schema, wird aber in diesem Zusammenhang gewöhnlich nicht als callback, sondern als event handler bezeichnet.

Einfache Rückruffunktion oder objektorientierter Rückruf[Bearbeiten]

Die sogenannten callback function in der Windows-API sind jeweils einfache C-Funktionen. Die Windows-API wird als C-Schnittstelle bereitgestellt. In Foren gibt es häufig Fragen nach der Möglichkeit, C++-Funktionen zuzuordnen. Notwendig ist hier eine Kapselung des C++-Aufrufes in einer C-Funktion, die die Dateninstanz kennt.

Anders ist es in objektorientierten Ansätzen eines Rückrufes. Statt der Angabe eines Funktionszeigers wird die Referenz auf ein Datenobjekt für den Rückruf übergeben. Das Datenobjekt muss ein für den Rückruf bestimmtes Interface realisieren. Das Interface enthält dann mehrere mögliche Rückruffunktionen in der Tabelle virtueller Methoden innerhalb der Dateninstanz und kennt gleichzeitig die notwendigen Daten für die Ausführung des Rückrufes. Das Grundschema ist genau das gleiche wie in den obigen Schemata gezeigt.

Beispiel für die Organisation eines einfachen Rückrufes im selben Ablauf[Bearbeiten]

Es soll eine Funktion apply_to geschrieben werden, die eine beliebige andere Funktion auf eine Liste von Werten anwendet und eine Liste der Ergebnisse zurückgibt. Eine Umsetzung in Pseudocode:

function apply_to (rückruffunktion, werte):
    var ergebnis; // Liste für die Ergebnisse

    foreach w in werte do // für alle Werte ...
        e = rückruffunktion (w); // rufe die Rückruffunktion ...
        ergebnis[] = e;   // und hänge das Resultat an die Ergebnisliste an.
    end;

    return ergebnis;
end apply_to;

Diese Funktion kann jetzt folgendermaßen verwendet werden:

function verdoppeln (wert): // dies ist eine Rückruffunktion
    return wert*2;
end verdoppeln;

function quadrat (wert): // dies ist eine andere Rückruffunktion
    return wert*wert;
end quadrat;

// Anwenden der Funktionen auf eine Liste:

werte = (1, 2, 3, 4);

doppelte_Werte   = apply_to(verdoppeln, werte); //ergibt (2, 4, 6, 8)
quadrierte_Werte = apply_to(quadrat, werte);    //ergibt (1, 4, 9, 16)

Oder, kürzer, in Lambda-Notation:

werte = (1, 2, 3, 4);

doppelte_Werte   = apply_to(lambda x: x*2, werte); //ergibt (2, 4, 6, 8)
quadrierte_Werte = apply_to(lambda x: x*x, werte); //ergibt (1, 4, 9, 16)

Siehe auch[Bearbeiten]

Weblinks[Bearbeiten]