Interface als Template-Parameter
Die in den Beispielen mit rein virtuellen Interfaces und nicht rein virtuellen Interfaces beschriebenen Aufwände und die damit verbundenen Risiken lassen sich durch die Anwendung von Template-Klassen eliminieren. Der Preis dafür ist der Verzicht auf die dynamische Polymorphie, die in vielen Embedded-Softwaresystemen nicht zwingend erforderlich ist. Es ergibt sich nur eine statische Polymorphie.
Der Interface-Zugreifer tcController bekommt als Template-Parameter die Objekt- / Interfacetypen CounterA_T und CounterB_T, deren Realisierungen cUpCounter und cDownCounter er adressieren möchte. Eine direkte Abhängigkeit im Programmcode zwischen den Software-Subsystemen Controller und Counter gibt es nicht mehr. Die indirekte Abhängigkeit entsteht durch die Template-Typisierung bei der Instanziierung. Die spezifizierten Typen cUpCounter und cDownCounter müssen alles bereitstellen, was der Zugreifer tcController aufruft. Ist das nicht der Fall, meldet dies bereits der Compiler als Fehler (kein Laufzeitfehler!).
Bild 13: Interface als Template-Parameter
Das Interface selbst ist bei diesem Interface-Design nicht eindeutig sichtbar und nur indirekt im Zugreifer durch dessen Aufrufe spezifiziert. Diese Problematik verbessert sich durch die Anwendung des Curiously Recurring Template Pattern (CRTP) [3].
Curiously Recurring Template Pattern (CRTP)
Bild 14: CRT-Pattern
Die Template-Klasse tcCounter bzw. deren Funktionen repräsentieren das komplette Interface und sind vom Zugreifer tcController aufrufbar:
mobjCounterA.count();
mobjCounterB.count();
Die Interface-Funktion count() der Interface-Template-Klasse tcCounter ruft als Delegate über den Template-Parameter Realization_T die count() Funktion der realisierenden Klasse tcUpCounter bzw. tcDownCounter auf:
template <typename Realization_T>
void tcCounter<Realization_T>::count()
{
static_cast<Realization_T*>(this)->count();
}
Der Template-Parameter Realization_T wird bereits direkt bei der Vererbung von tcCounter in tcUpCounter und tcDownCounter gesetzt:
class cUpCounter final : public tcCounter<cUpCounter>
class cDownCounter final : public tcCounter<cDownCounter>
Das Setzen eines Template-Parameters mit sich selbst als Klassentyp wird als MixedIn bezeichnet.
Resümee
Die Entscheidung für das „richtige“ Interface-Design ist immer von den geltenden Software-Anforderungen abhängig.
Interfaces unterstützen positiv die Umsetzung von Software-Qualitätsmerkmalen, wie beispielsweise Wiederverwendbarkeit, Portabilität, Austauschbarkeit und Erweiterbarkeit. Interface-Konzepte sind ein geeignetes Mittel zur Erfüllung von Software-Entwurfsprinzipien, z.B. lose Kopplung, Externalisierung von Abhängigkeiten, Modularisierung und Erreichen einer hohen Kohäsion.
Ein weiterführendes Konzept zu und mit Interfaces sind Ports. Ein Port vereint thematisch null bis unendlich viele bereitgestellte Interfaces und null bis unendlich viele erwartete Interfaces und lässt sich mit anderen kompatiblen Ports verbinden.