Das Semantik-Interface und die Funktionen
Die Interface-Typisierung spiegelt sich in den vergebenen <<Stereotypen>> und in den Interfacenamen als Prefix ic (interface), icb (interface callback) und icbreg (interface callback registration) wider. Als Interfacename eignet sich ein aussagekräftiges und bedeutungsvolles Substantiv.
In allen Interface-Typen sind die Funktionen öffentlich (public).
Bei Funktionen sind programmiersprachenabhängige Modifizierer wie virtual, const, static, inline, … zu berücksichtigen.
Als Funktionsnamen eigenen sich Verb-/Substantiv-Kombinationen. Bei Callback-Interface-Funktionen bietet sich als Verb melde (notify) an. Bei Callback-Registrierungsinterface-Funktionen bieten sich die Verben registriere (register) und de-registriere (unregister) an.
Funktionsparameter sind optional und sollten eine maximale Anzahl von sieben bis zwölf nicht überschreiten. Die Richtung ist durch in | out | inout vor dem Parameternamen visualisierbar. Unterstützt die Programmiersprache (z.B. C++) einen Default-Parameterwert, ist dieser spezifizierbar.
Die Reihenfolge der Funktionsparameter kann die Performance beeinflussen. Es sollte mit den Standard-Datentypen begonnen werden, da der Compiler diese per CPU-Register in die Funktion übergeben kann (sofern noch Register frei sind). Sobald ein komplexer Datentyp, wie beispielsweise eine Struktur, per
Wert übergeben wird, reicht der Compiler diesen und alle folgenden Parameter per Stack in die Funktion.
Als Parametername eignen sich ein Substantiv oder eine Kombination aus mehreren aussagekräftigen und bedeutungsvollen Substantiven.
Parameter– und Returntypen sind optional void. Als Datentypen können entweder Standard-Datentypen aus der Programmiersprache oder bereits definierte eigene Datentypen zum Einsatz kommen, jedoch keine, die erst in der Zukunft definiert werden.
Aus Gründen der Performance ist immer eine Übergabe als Zeiger / Referenz (keine Kopie!) gegenüber einer Übergabe als Wert (Kopie!) zu bevorzugen.
Daten-Typenamen sollten durch ein Postfix _t gekennzeichnet sein.
Wie bei Funktionen gibt es auch bei Typen programmiersprachenabhängige Modifizierer wie const, static, *, &,[], … .
Ansätze der Interface-Implementierung
Im einfachsten Fall ist ein Interface ein Header-File mit einer Summe deklarierter Funktionen. Der Zugreifer inkludiert das Interface-Header-File für den Aufruf der Interface-Funktionen. Ein oder mehrere realisierende Module inkludieren das Header-File zur Implementierung der Interface-Funktionen. Dieser einfachste Implementierungsansatz wird hier nicht weiter betrachtet. Vielmehr soll es um fortschrittlichere Implementierungsoptionen in C++ und, wo dies sinnvoll möglich ist, auch in C gehen.
Die zu Beginn vorgestellten Interface-Designs sind mit den folgenden Implementierungsansätzen umsetzbar:
Bild 3: Implementierungsansätze
Nicht-polymorphe Struktur bedeutet, dass das Interface genau eine Implementierung besitzt, während polymorphe Struktur das mehrfache Vorhandensein von Implementierung meint. Bei polymorphen Strukturen lässt sich die Art der Bindung zwischen Objekt / Funktionszeiger und Funktion unterscheiden. Dynamische Bindung (Bindung zur Laufzeit) ermöglicht einen vom Kontext abhängigen Aufruf verschiedener Interface-Funktionsimplementierungen. Bei der statischen Bindung (Bindung zur Compilezeit) bindet der Compiler bereits eine zur Laufzeit nicht veränderbare Interface-Funktionsimplementierung.
Die unterschiedlichen Implementierungsansätze lassen sich wie folgt bewerten:
Bild 4: Bewertung der Implementierungsansätze