"Program to an interface, not an implementation." Erstes Prinzip des objektorientierten Entwurfs, [GOF1995].Im vorigen Blogartikel haben wir Interfaces in vier Kategorien eingeteilt. Von diesen behandeln wir im vorliegenden Artikel totale 1:n-Interfaces:
- totale 1:1-Interfaces
- totale 1:n-Interfaces
- notwendig partielle Interfaces
- freiwillig partielle Interfaces
Vermeidung redundanter Fallunterscheidungen
Ein typischer Fall für die Verwendung totaler 1:n-Interfaces besteht in der Anwendung des Single Choice Prinzips. Im Rahmen der Behandlung dieses von Bertrand Meyer formulierten Prinzips hatten wir bereits festgestellt, dass die Vermeidung redundanter Fallunterscheidungen mittels Polymorphie und dynamischer Bindung einen positiven Einfluss auf eine Reihe von Qualitätsmerkmalen ausübt. Derartige Fallunterscheidungen können beispielsweise die Auswahl zwischen einer Familie ähnlicher Entitäten oder Algorithmen betreffen.Die Alternative bestünde in einem irgendwie gearteten Auswahlmechanismus für die konkreten Implementierungen, der sich möglicherweise redundant über zahlreiche Module verteilen würde. Das totale 1:n-Interface ermöglicht es den Aufrufenden, sich auf ihre eigene Verantwortlichkeit zu konzentrieren, während der Auswahlmechanismus für die Instantiierung der passenden Implementierung von einer anderen Einheit übernommen wird, die für diese Aufgabe besser geeignet ist. Als Auswahlmechanismus kommen beispielsweise eine abstrakte Fabrik, ein Objekt-Repository oder generische Programmiertechniken in Betracht.
Das Single Choice Prinzip liefert also ein starkes Ergänzungs-Kriterium zu Program to an Interface.
Offenheit für Erweiterungen
Mehrere Implementierungen stehen für mehrere Variationen eines bestimmten Sachverhalts oder Vorgangs. Dabei ist es grundsätzlich nicht unwahrscheinlich, dass zur Entwurfszeit nicht alle Variationen erkannt werden oder dass im Laufe der Benutzung des Systems neue Variationsmöglichkeiten entstehen. Es muss demnach auf einfache Weise möglich sein, neue Variationen hinzuzufügen. Dies ist eine zentrale Forderung des Open-Closed Prinzips.Angewandt auf die Terminologie von Aufrufern und Aufgerufenen ermöglicht es das Open-Closed Prinzip den Aufrufern, geschlossen zu bleiben, während das Interface offen für Erweiterungen (nämlich zusätzliche Implementierungen) bleibt. Welches andere Konstrukt könnte diese Offenheit gewährleisten? Im Katalogeintrag zum Open-Closed Prinzip wurden u. a. Closures, Plugin-Architekturen oder das Vorsehen von User-Exits genannt. Insofern solche Möglichkeiten naheliegen, können sie durchaus Alternativen zur Verwendung totaler 1:n-Interfaces sein. Dennoch ist die Verwendung von Interfaces in Sprachen, die abstrakte Typen zur Verfügung stellen, eine vergleichsweise einfach realisierbare Möglichkeit.
Das Open-Closed Prinzip liefert demnach ein weiteres Ergänzungs-Kriterium zu Program to an Interface.
Austausch per Konfiguration
Ein häufiger Spezialfall totaler 1:n-Interfaces sieht wie folgt aus:Zur produktiven Laufzeit des Systems wird ausschließlich die ProduktiveImplementierung als Aufgerufene verwendet. Unter anderen Ausführungsbedingungen, insbesondere im Rahmen der Ausführung von Tests oder während der Entwicklungsarbeit kann jedoch auch Mock zum Einsatz kommen. Der Austausch der Implementierung erfolgt in einer solchen Konstellation oftmals durch die Konfiguration der Ausführungsumgebung wie beispielsweise test- oder entwicklungsspezifische Dependency Injection. Während zur Laufzeit eigentlich ein totales 1:1-Interface vorliegt, enthält die Codebasis doch mehrere Implementierungen, so dass der Charakter eines totalen 1:n-Interfaces entsteht.
Zusammenfassung
Polymorphie und dynamische Bindung bilden die technische Grundlage für die elegante Lösung der besprochenen Konstellationen:- redundanzfreie Fallunterscheidungen zur Laufzeit des Systems (Single Choice Prinzip)
- Offenheit für Erweiterungen über die Lebenszeit des Systems (Open-Closed Prinzip)
- Austausch von Implementierungen zur Konfigurationszeit des Systems (z. B. per Dependency Injection)