Der Einsatz von Software-Interfaces ist ein elementares Mittel zur Entwicklung von langlebigen und tragfähigen Software-Architekturen. Deshalb sollten sie so früh wie möglich in der Architektur etabliert werden, um diese zu stabilisieren. Der Software-Architekt kann so eine schnelle Aufgabenverteilung auf unabhängige Personen, Teams oder Standorte ohne weitere „Reibungsverluste“ sicherstellen.
Welche Varianten Sie beim Interface-Design kennen sollten und wie diese in den Programmiersprachen C und C++ implementierbar sind, verrät dieser Beitrag.
Das Interface-Konzept und Designvarianten
Ein Software-Interface stellt eine Summe von Funktionen mit der kompletten Semantik (Name, Parameter, Parametertypen, Rückgabetypen, spezielle Modifizierer) für den Zugriff und die Realisierung bereit.
Mindestens ein Element (Accessor) greift auf das Interface zu. Der Zugreifer erwartet das Interface (Required Interface).
Mindestens ein Element (Realization) muss das Interface implementieren. Die Realisierung stellt das Interface bereit (Provided Interface).
Das Interface dient zur Entkopplung zwischen dem Zugreifer und der Realisierung, weiter übergeordnet zur Entkopplung zwischen Architekturelementen.
Bild 1: Interface-Konzept
Bei verschiedene Designvarianten zwischen Zugreifer und Interface(es)
- greift ein Zugreifer auf ein oder mehrere Interfaces zu
- greifen mehrere Zugreifer auf ein oder jeweils ein Interface zu
- greifen mehrere Zugreifer auf ein oder mehrere Interfaces zu
- greifen mehrere Zugreifer auf unterschiedliche Interface-Ebenen zu oder
- greifen mehrere Zugreifer auf jeweils unterschiedliche Interface-Ebenen zu
Bei den Designvarianten der Interface-Realisierung sind möglich:
- ein Interface mit einer Realisierung
- ein Interface mit mehreren oder partiellen Realisierungen
- mehrere Interfaces in einer oder mehreren Realisierungsvarianten
Interface-Typisierung
Interfaces lassen sich (abhängig von verschiedenen Zugreifern) thematisch aufteilen, beispielsweise pro Architekturelement jeweils eines für die Konfiguration, die Diagnose und den Normalbetrieb. Generell lassen sich Interfaces in drei unterschiedliche Typen kategorisieren:
- Das Call-Interface bietet dem Zugreifer aus dem Architekturelement A beispielsweise Funktionen an, um Werte aus dem Architekturelement B zu lesen, Werte hineinzuschreiben oder dort Algorithmen auszulösen.
- Das Callback-Interface meldet aktiv neue Werte oder Ereignisse vom Architekturelement B an A. Bei der Struktur ist zu beachten, dass die Realisierung des Callback-Interfaces über Architekturelement-Grenzen hinweg geht und sich hier in Architekturelement A befindet. Im Zusammenspiel mit dem Call-Interface ist so in der Summe immer noch eine unidirektionale Abhängigkeit zwischen den beiden Architekturelementen A und B erreichbar.
- Falls die Registrierung des Callbacks dynamisch zur Laufzeit und nicht statisch zur Compilezeit durchgeführt wird, ist dafür ein spezielles Element (Manager) in der Software-Architektur verantwortlich. Dieser Manager kann mittels eines speziellen Callback-Registrierungsinterfaces ein oder mehrere Callback-implementierende Elemente registrieren und de-registrieren.
Zur Umsetzung der in Bild 2 dargestellten Struktur lässt sich das Observer-Pattern anwenden.
Das konkrete Subjekt erfährt einen neuen Wert. Durch den Aufruf einer entsprechenden Funktion aus dem Observer meldet das konkrete Subjekt allen zuvor registrierten konkreten Observern den neuen Wert. Das Observer-Pattern hat sich inzwischen zu einem sehr populären Pattern für Embedded-Software entwickelt.
Bild 2: Callback-Struktur mit Registrierungsinterface