Donnerstag, 29. Mai 2014

Program to an Interface - Fazit und Gegenbeispiele

"Program to an interface, not an implementation." Erstes Prinzip des objektorientierten Entwurfs, [GOF1995].
In den vorangehenden Blogartikeln wurden Interfaces zunächst in vier Kategorien eingeteilt. Anschließend wurde für jede dieser Kategorien besprochen, worin die Motivation für deren Einsatz besteht, welche Alternativen es gibt und wann ggf. von einem Einsatz abzusehen ist:
Hier soll nun ein kurzes Fazit erfolgen und insbesondere nocheinmal die Frage gestellt werden, unter welchen Bedingungen der Einsatz eines Interfaces gerade nicht angebracht sein könnte.

Fazit: Bedingungen für den Einsatz eines Interfaces

Die klarsten Bedingungen, die für den Einsatz eines Interfaces sprechen, fanden wir bei totalen 1:n-Interfaces und bei notwendig partiellen Interfaces vor. Zusätzlich sind in diesen Konstellationen keine nennenswerten Nachteile durch den Interface-Einsatz erkennbar, die nicht auch in alternativen Lösungsansätzen auftreten können.
  • Totale 1:n-Interfaces bieten durch den Einsatz von Polymorphie und dynamischer Bindung eine einfache und elegante Möglichkeit, die Austauschbarkeit und Auswählbarkeit von Implementierungsteilen herzustellen (Single Choice Prinzip) und die Offenheit für weitere Implementierungsteile (Open-Closed Prinzip) zu gewährleisten.
  • Notwendig partielle Interfaces stellen eine in statisch typisierten objektorientierten Programmiersprachen natürliche und leistungsfähige Möglichkeit dar, als Platzhalter in der Implementierung eines Aufrufers verwendet zu werden.
Die dritte Interface-Kategorie, die ebenfalls wohlbegründet sein kann, wird von den freiwillig partiellen Interfaces gebildet. Die hier im Fokus stehende Zugriffsbeschränkung kann allerdings im Einzelfall ein weniger zwingendes Argument für den Einsatz darstellen. Neben den zusätzlichen Aufwänden für die Einführung und Pflege des Interfaces sind insbesondere einige potentielle Nachteile zu berücksichtigen, die gegen den Einsatz sprechen können.

Diejenige Interface-Kategorie, die in der Praxis am häufigsten vermieden werden dürfte, sind die totalen 1:1-Interfaces. Das einzige Argument für diese Interface-Kategorie besteht in der Entkopplung der Aufrufer von der Implementierung. Insofern durch diese Entkopplung kein arbeitsteiliger Entwicklungsprozess unterstützt wird, kein modellgetriebener Entwicklungsansatz besteht und keine Entkopplung des Deployments erforderlich ist, haben wir die erforderliche Abwägung mittels der Formel

Kosten für das Interface < Erwartete Austauschkosten

ausgedrückt. Sind sowohl die Austauschwahrscheinlichkeit als auch die Austauschkosten im Falle eines Austauschs gering, so ist die Einführung eines Interfaces hier eher zu vermeiden. 

Typische Beispiele für sinnvollen Verzicht auf Interfaces

Verzicht bei enger Kopplung
Besteht zwischen zwei Klassen inhaltlich eine sehr enge Kopplung, so erscheint die Einführung eines Interfaces (insbesondere wenn es sich um ein 1:1-Interface handelt, also nur genau eine Implementierung existiert), als überflüssiger Mehraufwand. Wenn es beispielsweise keinen Sinn ergibt, den Aufrufer ohne den Aufgerufenen zu testen, so ist dies ein klarer Hinweis auf eine solche enge Kopplung. 

Klassen des Domänenmodells
Für die Klassen eines Domänenmodells (insbesondere wenn es sich um persistente Entitäten ohne Verhalten handelt) kann die Nutzung von Interfaces überflüssig sein. Das Domänenmodell einer Anwendung ist oftmals sehr stabil, und falls die Entitäten nicht über eigenes Verhalten verfügen, so ist der künftige Austausch einer Implementierung praktisch auszuschließen.

Stabile Hilfsklassen
In jeder Anwendung existiert eine Anzahl relativ stabiler Hilfsklassen, die oftmals als Singleton oder über statische Methoden genutzt werden. Die Art dieser Hilfsdienste kann z. T. so konkret sein, dass ein künftiger Austausch der Implementierung sehr unwahrscheinlich ist. Stellen wir uns beispielsweise eine Klasse mit Validierungsmethoden für Benutzereingaben vor. Derartige Methoden nehmen oft simple Musterprüfungen oder Prüfungen von Wertebereichen vor, für die nur schwer vorstellbar ist, dass sie als gesamte Implementierung einmal ausgetauscht werden müssen.

Performance-Gründe
Gerade im Umfeld der Entwicklung für mobile Endgeräte, wo Performance immernoch ein zentrales Entwurfsziel ist, trifft man gelegentlich auf die Empfehlung zum Verzicht auf Interfaces. Gemäß [Alpern2001] sind drei Gründe zu identifizieren, die eine potentielle Performance-Verschlechterung durch die Nutzung von Interfaces bewirken können:
  • ein Overhead durch Typprüfung
  • der komplexere Dispatching-Mechanismus für Interface-Methoden
  • fehlende Optimierungen
Die genannte Untersuchung kommt zu dem Ergebnis, dass die Unterschiede lediglich in sehr frühen Java-Versionen signifikant waren und zum Zeitpunkt der Untersuchung (2001) für die meisten Anwendungsfelder zu vernachlässigen seien ("Invokeinterface considered harmless"). Mir ist leider keine aktuelle Studie zu diesem Thema bekannt. Es sollte daher sehr genau überlegt und im Zweifel überprüft werden, ob Interfaces einem fragwürdigen Performance-Gewinn geopfert werden sollten.

Hinweis: Für die Schilderung weiterer Konstellationen, in denen ein Verzicht auf die Nutzung von Interfaces sinnvoll sein könnte, wäre ich sehr dankbar. Ich würde die hier vorgestellte Beispiel-Liste gerne entsprechend erweitern.

Siehe auch

Alle Artikel der Serie "Program to an Interface ...":

Einführung
Kategorisierung von Interfaces
Totale 1:n-Interfaces
Totale 1:1-Interfaces
Freiwillig partielle Interfaces
Notwendig partielle Interfaces
Fazit und Gegenbeispiele
Interface oder abstrakte Klasse?
Werkzeugunterstützung
Das Prinzip

Quellen

[Alpern2001] - Efficient Implementation of Java Interfaces - Invokeinterface considered harmless, B. Alpern, A. Cocchi, S. Fink, D. Grove, D. Lieber, OOPSLA '01 Proceedings of the 16th ACM SIGPLAN conference on Object-oriented programming, systems, languages, and applications, Seiten 108-124 (2001)
[GOF1995] - Design Patterns - Elements of Reusable Software, E. Gamma, R. Helm, R. Johnson, J. Vlissides, (Addison‐Wesley, 1995)