"Program to an interface, not an implementation." Erstes Prinzip des objektorientierten Entwurfs, [GOF1995].Im vorletzten Blogartikel haben wir Interfaces in vier Kategorien eingeteilt. Von diesen behandeln wir im vorliegenden Artikel totale 1:1-Interfaces:
- totale 1:1-Interfaces
- totale 1:n-Interfaces
- notwendig partielle Interfaces
- freiwillig partielle Interfaces
Das Reused Abstractions Prinzip als Gegenargument
- redundanzfreie Fallunterscheidungen zur Laufzeit des Systems (Single Choice Prinzip)
- Offenheit für Erweiterungen über die Lebenszeit des Systems (Open-Closed Prinzips)
- Austausch von Implementierungen zur Konfigurationszeit des Systems (z. B. per Dependency Injection)
"Die Entdeckung von Abstraktionen impliziert, dass die Abstraktionen mehr als ein Mal verwendet werden. Superklassen haben mehr als eine Subklasse. Interfaces werden mehr als ein Mal implementiert. Abstrakte Methoden werden mehr als ein Mal überschrieben. Und so weiter." [Gorman2010]
Argumente für totale 1:1-Interfaces
Erinnern wir uns an die grundlegende Motivation für die Einführung totaler Interfaces. Diese besteht darin,"die Spezifikation des Aufgerufenen von seiner Implementierung zu trennen, so dass letztere ausgetauscht werden kann, ohne dass die Aufrufer davon Notiz nehmen müssen" [Steimann2005]Der hier gemeinte Austausch kann sich bei totalen 1:1-Interfaces offensichtlich nur auf die Lebenszeit des Systems beziehen. Unter welchen Umständen wäre es aber nachteilig, dann einfach die Implementierung selbst auszutauschen? Folgende Szenarien sind denkbar:
- Interface und Implementierung könnten von zwei verschiedenen Teams entwickelt werden. In diesem Fall stellt das Interface eine Vorgabe dar, welche von der Implementierung erfüllt wird. Die Nutzung des Interfaces dient hier der arbeitsteiligen Software-Konstruktion.
- Das Interface könnte aus einem modellgetriebenen Entwicklungsprozess heraus entstanden sein, der beispielsweise die Definition öffentlicher Schnittstellen in UML verlangt, um daraus die entsprechenden Interfaces zu generieren. Auf diese Weise kann z. B. sichergestellt werden, dass jederzeit ein aktuelles UML-Modell der zentralen Interfaces der Anwendung existiert, das den Architekten einen Überblick des Gesamtsystems gewährleistet.
- Das Interface könnte in einer anderen Deployment-Einheit enthalten sein als die Implementierung. Dies könnte z. B. dann der Fall sein, wenn es Gründe gibt, das Deployment der Implementierung vom Deployment des Interfaces zu entkoppeln. Eine solche Entkopplung kann im Rahmen des Abhängigkeits-Managements der Deployment-Einheiten wünschenswert sein, um Abhängigkeiten zu reduzieren oder separat zu versionieren.
- Die Implementierung könnte eine hohe Austauschwahrscheinlichkeit aufweisen. Dies ist beispielsweise bei Prototypen der Fall oder dann, wenn künftige gravierende Änderungen bereits bekannt sind.
- Der Austausch einer Implementierung gegen eine andere könnte aus irgendeinem anderen Grund zu hohen Folgeaufwänden führen, etwa dann, wenn die neue Implementierung die alte nicht einfach ersetzen kann und die daraus resultierende Umbenennung mittels der verfügbaren Refactoring-Werkzeuge nicht problemlos durchgeführt werden kann. In diesem Fall könnte die Einführung eines Interfaces auch dann sinnvoll sein, wenn die Austauschwahrscheinlichkeit eher gering ist, da der zusätzliche Aufwand für die Einführung solcher Interfaces gegenüber den potentiell hohen Kosten zu vernachlässigen ist.
Eine Kosten-Ungleichung
Die Abwägung, die in den beiden letztgenannten Punkten vorgenommen wird, läuft auf folgende Kosten-Ungleichung hinaus:Kosten für das Interface < Erwartete Austauschkosten
Trifft diese Bedingung zu, so ist die Einführung des Interfaces als lohnenswert zu betrachten. Dabei berechnen sich die Erwarteten Austauschkosten als das Produkt der Austauschwahrscheinlichkeit für die Implementierung mit den tatsächlichen Austauschkosten im Falle eines Austauschs der Implementierung, also:
Erwartete Austauschkosten = Austauschwahrscheinlichkeit * Austauschkosten
Zusammenfassung
Die Einführung totaler 1:1-Interfaces muss wohlbegründet sein. Als Gründe kommen arbeitsteilige Aspekte, modellgetriebene Entwicklungstechnologien, Entkopplung des Deployments, eine hohe Austauschwahrscheinlichkeit oder unvermeidbar hohe Kosten im Falle eines Austauschs in Betracht. Als Abwägungshilfe kann im Einzelfall die oben genannte Kosten-Ungleichung dienen.Siehe auch
Alle Artikel der Serie "Program to an Interface ...":Quellen
[GOF1995] - Design Patterns - Elements of Reusable Software, E. Gamma, R. Helm, R. Johnson, J. Vlissides, (Addison‐Wesley, 1995).[Gorman2010] - Jason Gormans Blogartikel "Reused Abstractions Principle (RAP)" - http://codemanship.co.uk/parlezuml/blog/?postid=934
[Steimann2005] - Patterns of interface‐based programming, F. Steimann, P. Mayer, Journal of Object Technology 4:5, 75–94 (2005), http://www.jot.fm/issues/issue_2005_07/article1.pdf