Interfaces vs abstrakte Klassen
Am 13. Oktober 2008 haben Golo Roden und ich unter dem Titel Noch Fragen, Roden? Ja, Bucher! angekündigt, jeweils zum ersten eines jeden Monats einen Kommentar zu einem vorab gemeinsam gewählten Thema verfassen zu wollen. Bisher sind in dieser Reihe folgende Kommentare erschienen:
Heute, am 1. April 2009, ist es nun wieder so weit, und unser Thema für diesen Monat lautet:
Interfaces vs abstrakte Klassen
So wohl Golo wie auch ich haben uns unabhängig voneinander im Vorfeld unsere Gedanken gemacht, wie wir diesem Thema gegenüberstehen. Golos Kommentar
findet sich zeitgleich in seinem Blog, folgend nun mein Kommentar zu diesem Thema:
Interface vs Schnittstelle
Wenn ich im folgenden Text von einem “Interface” spreche, spreche ich vom C# Schlüsselwort “interface”.
Mit “Schnittstelle” hingegen könnten eine Reihe von öffentlichen Methoden / Eigenschaften / Events einer API beschrieben werden,
die man benutzen kann, ohne die Implementation – also die Logik dahinter – zu kennen.
Eine normale Klasse die öffentliche Members (Mitglieder) hat, kann mit diesen also auch als Schnittstelle angesehen werden.
Beide, die Interfaces und die abstrakten Klassen können Schnittstellen abbilden, die von anderen Klassen
implementiert bzw. geerbt werden kann.
Ein paar Eigenschaften um die zwei Kontrahenten zu unterscheiden:
Interfaces
- Alle Members müssen implementiert werden (Interface Members sind standardmässig abstract).
- Enthalten nur öffentliche Members.
- Eine Klasse kann mehrere Interfaces implementieren (Polymorphie über verschiedene Klassen (Hierarchien).
- Beschreiben einen Vertrag / Abmachung.
- Ein Interface ist nicht veränderbar, bei einer Veränderung bricht der ganze Clientcode, da dieser komplett angepasst werden müsste [Versioning Issues With Abstract Base Classes and Interfaces].
- Beschreibt eine has-a (hat ein) Beziehung.
- Kann nicht instanziiert werden (Nur Benutzung des Types selber).
Abstrakte Klassen
- Kann schon Implementation enthalten.
- Zwang zur Implementation für die erbende Klasse per abstract-Schlüsselwort möglich.
- Optionale Implementation für die erbende Klasse per virtual-Schlüsselwort möglich.
- Nur Einfachvererbung möglich.
- Beschreiben einen Vertrag / Abmachung.
- Die abstrakte Klasse ist veränderbar, jedoch nur zu einem gewissen Grad und auch umständlich [Abstract Base Classes Have Versioning Problems Too].
- Beschreibt eine is-a (ist ein) Beziehung.
- Kann nicht instanziiert werden (Nur Benutzung des Types selber).
Wann jetzt also was verwenden?
Stellen wir uns mal vor, dass wir ein Content Management System entwickeln, dort gibt es folgende Klassen:
Content
|-> Article
|-> BlogPost
|-> Download
Wir haben eine Basisklasse “Content”. Jetzt ist zuerst mal die Frage ob diese Klasse abstrakt sein soll, oder nicht.
Da wir die Klasse “Content” nicht direkt in unserer Logik als eine logische Einheit benutzen, also nie eine Instanz davon erzeugen möchte weil es keinen Sinn macht, sollten wir eine abstrakte Klasse verwenden.
Das heisst, in unserem öffentlichen System gibt es Artikel, Blogposts und Downloads, jedoch nirgendwo “Contents”.
Die Klasse “Content” wird dann benutzt um polymorphes Verhalten bei den Unterklassen zu benutzen.
Content
.Publish()
|-> Article
|-> BlogPost
|-> Download
Die Basisklasse “Content” hat jetzt eine Methode / ein Verhalten “Publish”.
Wenn dieses Verhalten bei allen Unterklassen gleich aussehen sollte, kann man das Verhalten einmal in der Basisklasse definieren und wiederverwenden, das spricht für die abstrakte Klasse, da sie Implementationen enthalten kann.
Wenn die Verhalten für die Methode “Publish” in den Unterklassen verschieden sind, bzw. nicht mal alle Unterklassen dieses Verhalten haben müssen, ist ein Interface zu empfehlen.
Ganz pragmatisch…
..betrachtet sollte in den meisten Fällen einer abstrakten Klasse den Vorzug gegeben werden.
Die Entscheidungskette kann also so aussehen:
Eine Basis für ein Set von ähnlichen Klassen.
Wird die Basis auch als logische Einheit verwendet (Selber instanziiert)?
–> normale Klasse
-> Ansonsten abstrakte Klasse.
Gibt es Verhaltenszüge, die nur einzelnen Mitgliedern dieses Sets vorenthalten werden sollen, bzw. über mehrere verschiedene Sets gleich aussehen und polymorph genutzt werden soll?
-> Interface
Wieso nicht beides? => Interface _und_ eine abstrakte Klasse?
Kann Sinn machen, jedoch nicht in vielen Fällen.
Wenn beides besteht, welcher Typ soll dann überall verwendet werden?
Die abstrakte Klasse oder das Interface?
Wenn der Typ der abstrakten Klasse überall angegeben wird (Methoden erwarten bspw. diesen Typ), hat das Interface keinen Nutzen mehr.
Wird jedoch der Typ des Interfaces angegeben, was für einen Sinn macht dann die abstrakte Basisklasse?
Eine abstrakte Basisklasse macht insofern Sinn, dass sie eine mögliche Standard-Vorimplementierung enthält, die man direkt nutzen kann.
Man halt als Entwickler also die Möglichkeit entweder das Interface zu implementieren oder die schon vorhandene Implementation in der Basisklasse zu nutzen indem man davon erbt.
In beiden Fällen ist der Typ dann mit den Interface kompatibel und die Geschichte ergibt auch einen Sinn.
Links / Quellen: