• Abstrakte Klassen vs. Interfaces – 0:1

    von am 15. Mai 2009

    Heute wollen wir mal einen kleinen Wettstreit zwischen abstrakten Klassen und Interfaces starten. Vielleicht mache ich ja eine Reihe draus. Mal schauen wie es ankommt. Da es in der PHP Gemeinde leider nicht sehr verbreitet ist Interfaces einzusetzen, möchte ich doch mal eine Lanze brechen und mal einen Vorteil der Interfaces gegenüber anderer Objekt­orientierter Konstrukte aufzeigen.

    Ich kann mich noch an eine Diskussion an einen Ex-Arbeitskollegen erinnern, der meinte, dass abstrakte Klassen alles hergeben, was ich mit Interfaces machen kann. Sie wären sozusagen unnötig. Ich hoffe, dass er dies liest und morgen anderer Meinung ist. Naja vielmehr hoffe ich, dass ich mein Argument so gut formulieren kann, dass er es kapiert.

    Also fangen wir an. Interfaces sind dazu da das Verhalten einer Klasse nach außen festzulegen. Wenn ich das Interface kenne, gegen das eine Klasse programmiert wurde, so muss mich nicht interessieren, wie es umgesetzt wurde. Ich kann es einfach verwenden. Und das tolle daran ist:  jede Klasse, die dieses Interface implementiert kann einfach durch eine andere ausgetauscht werden. Super Sache. Aber die Verfechter von abstrakten Klassen werden jetzt sagen: “Moment mal, das kann ich auch!”. Was macht man in einem solchen Fall? Beispiele zeigen.

    interface Countable
    {
      public function count( );
    }
    
    abstract class Countable
    {
      abstract public function count( );
    }

    Tja dumm gelaufen. Diese zwei Varianten sind wirklich gleich mächtig. Beide zwingen einen dazu die Funktion count zu implementieren und beide können beim Typehinting verwendet werden. Ich würde mal sagen, es steht also immer noch 0:0 in unserem kleinen Wettstreit. Was machen wir jetzt aber wenn wir noch ein “Interface” haben. Nehmen wir ein Sortable Interface.

    interface Sortable
    {
      public function sort( );
    }
    
    abstract class Sortable
    {
      abstract public function sort( );
    }
    

    Und wieder ist es genau das gleiche. War ja auch zu erwarten, ich habe ja einfach nur die Namen ausgetauscht. Jetzt will ich aber eine Klasse haben, die sowohl Countable als auch Sortable ist. Tooooor. 0:1 für das Interface. Interfaces haben die wunderbare Eigenschaft, dass hier “Mehrfachvererbung” geht, was bei Klassen nicht funktioniert. Was auch gut ist, denn bei Klassen bekommt man leicht Probleme dabei (siehe Diamantenproblem).  Ich kann also ohne Probleme beide Interfaces implementieren. Sobold ich mich aber für eine Superklasse (Elternklasse) entschieden habe, habe ich mir den kompletten Abteilungsbaum zugemacht.

    Wenn es unbedingt nötig sein sollte, kann man ja getrost trotzdem von der abstrakten Klassen ableiten. Das natürlich nur, wenn dort schon Funktionalitäten verortet sind, die man benötigt. Abstrakte Klassen haben aber auch ihr Einsatzgebiet, dass ich euch aber ein ander mal verraten werde. Ich habe ja schließlich noch eine Menge von Artikel vor mir.

    Eine Sache möchte ich euch noch mitgeben: Benutzt Interfaces!!! Die Dinger sind so schön und nützlich.

    Nils Langner

    Auch wenn Ihr es mir nicht glauben werdet, aber ich habe nichts gegen PHP. Ich rege mich einfach nur gerne auf. Ok so schlimm ist es auch nicht. Eigentlich wollte ich schon immer einen Blog haben und da ...

    Zum Profil von Nils Langner

    16 Kommentare »


    • Cem Derin
      am 15. Mai 2009 um 07:24 Uhr

      Ah, wieder ein Artikel auf den ich mit einem kompletten blogpost Ringehen kann. Mal sehen, ob ich dazu heute noch Zeit finde ;-)


    • Joe Scylla
      am 15. Mai 2009 um 08:15 Uhr

      Wer statt Interfaces abstrakte Klassen verwendet verliert die Möglichkeit beides zu kombinieren.

      interface iObservable
      	{
      	public function registerObserver($params);
      	}
      abstract class Observable implements iObservable
      	{
      	private $observers = array();
      	public function registerObserver($params){}
      	}
      interface iMail
      	{
      	public function setSubject($subject);
      	public function setBody($body);
      	}
      abstract class Mail extends Observable implements iMail
      	{
      	private $subject = "";
      	private $body = "";
      	public function setSubject($subject){$this->subject = $subject;}
      	public function setBody($body){$this->body = $body;}
      	abstract public function send();
      	}
      class MailSmtp extends Mail
      	{
      	public function send(){}
      	}
      class MailSendmail extends Mail
      	{
      	public function send(){}
      	}
      

    • Nils Langner
      am 15. Mai 2009 um 08:18 Uhr

      @Cem: Ich bin gespannt. Werde bestimmt wieder einen Kommentar finden ;)


    • Cem Derin
      am 15. Mai 2009 um 08:23 Uhr

      Wäre ja schade, wenn immer alles unkommentiert bleibt ;-)


    • rniederer
      am 15. Mai 2009 um 08:26 Uhr

      Wir haben Interfaces und Abstrakte Klassen in der Schule ein wenig angeschaut, jedoch nicht so sehr vertieft…

      Sehe ich das jetzt richtig:
      Ein Interface kann von einem Interface ableiten. Eine Abstrakte Klasse kann jedoch nicht von einer Abstrakten Klasse abgeleitet werden?


    • Nils Langner
      am 15. Mai 2009 um 09:12 Uhr

      @rniederer: Ne das stimmt nicht :) Ich kann gegen MEHRERE Interfaces prorammieren aber maximal von EINER Klasse ableiten.

      Natürlich könnte ich in meinem Beispiel beide abstrakte Klassen hintereinander Ableiten, aber was machst du, wenn du nur eine Schnittstelle implementieren willst? Das wird dann nicht gehen.


    • Johannes
      am 15. Mai 2009 um 09:26 Uhr

      Und ganz wichtig: Zwischen von einander abgeleiteten Klassen beseht eine “ist-ein” Beziehung also um mit dem klassischen Beispiel class Auto extends Fahrzeug {} – ein Auto ist ein Fahrzeug und hat alle Eigenschaften eines Fahrzeuges und kann alles was ein Fahrzeug kann. Das interface sagt nur, dass das implementierende ene Fähigkeit hat.

      Entscheiden be abstrakten Klassen ist auch: Die kann gewisse Funktionalität implementieren, das Interface beschreibt wirklich nur die Schnittstelle.


    • Nils Langner
      am 15. Mai 2009 um 09:42 Uhr

      @Johannes: Schön gesagt :) Auf den zweiten Teil, den mit der Funktionalität, werde ich noch mal in dem 1:1 Artikel aufnehmen.


    • rniederer
      am 15. Mai 2009 um 09:43 Uhr

      @Nils:
      Danke…

      @Johannes:
      Danke, das weiss ich… ein bisschen hab ich in der Schule auch aufgepasst resp. ein bisschen wurde uns auch was beigebracht ;-)
      Mir war nur das mit der Vererbung resp. Ableitung nicht ganz klar :D


    • PHPDave
      am 15. Mai 2009 um 19:57 Uhr

      Ein 1:1 oder sogar 2:1 (wenn dein Artikel zu 1:1 geschrieben ist) dürfte aber die Implementierung von Attributen bei abstrakten Klassen liefern :) (was in Interfaces nicht möglich ist)


    • straffi
      am 15. Mai 2009 um 20:14 Uhr

      Moin Gemeinde,

      Joe -
      hats voll getroffen. So ähnlich siehts bei mir auch meistens aus.
      Eine abstrakte Klasse, die 90% der Standardfunktionalität implementiert, und ein passendes Interface dazu, das die
      benutzenden Klassen von genau dieser Implementierung löst.

      Das gibt dem Entwickler die Möglichkeiten, die Standardmethoden (geerbt von der abstrakten Klasse) anzupassen, oder eine komplett eigenständige Implementierung – eventuell sogar mit einer neuen abstrakten Klasse – drüberzubügeln.

      Johannes -
      hat genau die Denkweise erkannt, die ich (leider) immer wieder -und gehäuft bei Sprachen die OOP erst im Nachhinein bekamen (PHP/VB/Delpi/Perl…) – antreffe.

      Es gab halt irgendwann mal ein Tutorial über Klassen und eine Beispielimplementierung (MySQL oder Mail oder http://FTP...), die genau diese eine Möglichkeit der Vererbung nutzt.
      Als Fazit bleibt dem Neuling das Gefühl, jetzt endlich auch mal dass Schlüsselwort “class” benutzt zu haben und somit ein OO-Design zu haben.
      Ich nenne das PWO (Programming-With-Objects), anstatt OOP, da die Objekte im Endeffekt nur als erweiterte includes benutzt werden.

      mfg
      straffi

      denic-ergebnis diese posts:
      – Die Domain “drueberzubuegeln.de” ist nicht registriert
      – Die Domain “programming-with-objects.de” ist nicht registriert.


    • rniederer
      am 16. Mai 2009 um 08:56 Uhr

      Nun, ich bin programmiere nun noch nicht sooo lange Objekt orientiert und in der Schule kann uns der Lehrer keine genauen Beisipiele geben…

      -> Kann mir jemand ein Beispiel aufzeigen, bei dem man abstrakte Klassen oder Interfaces braucht…


    • Thomas
      am 18. Mai 2009 um 14:56 Uhr

      Hallo Nils,

      Du schreibst:

      “Interfaces haben die wunderbare Eigenschaft, dass hier “Mehrfachvererbung” geht, was bei Klassen nicht funktioniert.”

      Ich finde, “Mehrfachvererbung” ist hier die so ziemlich unpassendste Formulierung. #7 und #11 treffen’s wohl am Besten:

      Erbt man von einer abstrakten Klasse, so hat die erbende Klasse (erstmal) automatisch alle Eigenschaften der abstrakten Klasse. Die Eigenschaften und Fähigkeiten können unter Umständen nicht einmal überschrieben werden.

      Ein Interface hingegen gibt tatsächlich lediglich vor, dass eine implementierende Klasse die im Interface definierten Eigenschaften und Fähigkeiten besitzen muss. Hier sehe ich insbesondere bei großen Projekten einen weiteren Nutzen:

      Wenn ich (als Entwickler) Klassen eines Moduls eines Anderen verwenden will/muss, schaue ich mir lediglich die Dokumentationen seiner Interfaces an. Wie seine konkreten Klassen, die ich letztendlich benutze, funktionieren, braucht mich nicht zu interessieren.


    • Luis
      am 25. Juni 2009 um 23:26 Uhr

      Hallo Nils und Thomas,
      ich finde der Einwand von Thomas richtig. Prinzipiell sind Schnittstellen in weitere Sinne Verträge zur Benutzung von Modulen, Bibliotheken … etc. Solche Schnittstellen gewährleisten die Unabhängigkeit der Software von der Umgebung, bestehen vorwiegend aus Spezifikationen und können auf viele verschiedene weisen implementiert werden (selbst ohne interfaces in den Sprachen, die nicht darüber verfügen). Nun manche Sprachen bieten das Konstrukt der interface, mit dem man solche Benutzungsverträge gut implementieren kann.

      Den Ergebnis hat Thomas bereits sehr schön ausgedruckt:

      “Wenn ich (als Entwickler) Klassen eines Moduls eines Anderen verwenden will/muss, schaue ich mir lediglich die Dokumentationen seiner Interfaces an. Wie seine konkreten Klassen, die ich letztendlich benutze, funktionieren, braucht mich nicht zu interessieren.”


    • Nick
      am 6. Juli 2010 um 14:43 Uhr

      Halli Hallo,
      bin gerade durch Zufall drüber gestolpert.
      Danke für den Artikel. Ich muß mich dem Thomas anschließen, auch auf die Gefahr hinaus ein Haar zu spalten: Interfaces werden implementiert und nicht vererbt.

      Ich würde Jederzeit ein Interface einer abstrakten Klasse vorziehen, solange in der Klasse nur Methoden ohne Implementierung vorgesehen sind. Sobald es aber an Felder und vollständig implementierte Methoden geht, warum nicht die abstrakte Klasse verwenden? Sie hat durchaus ihren Anwendungsfall und diese Linie zu ziehen: wann verwende ich was ist das eigentlich Interessante.


    • zvz
      am 1. Juni 2011 um 17:55 Uhr

      Gibt es Performance-Unterschied zwischen Verwendung von abstrakten Klassen und Verlängerung normale Klasse?

    RSS Feed für Kommentare zu diesem Artikel. TrackBack URL

    Hinterlasse einen Kommentar

    Werbung
    PHP Magazin
    Ausgabe 02/2010

    Dieses Mal mit Artikeln zu den Themen OpenSocial und Apache Shindig, Graphentheorie, Smarty3

    t3n
    Ausgabe 19

    Social Media (R)evolution. Weitere Themen sind noSQL, Crowdsourcing ...

    PHP Journal
    Ausgabe 2/2010

    PHP & Windows optimal nutzen, die besten PHP-CMS im Überblick, Google-API mit Zend Framework nutzen.

    Wir wurden schon öfters gefragt, ob man uns nicht irgendwie unterstützen kann. Die Antwort war immer einfach: Klar! Am einfachsten ist es eure nächsten Einkäufe bei Amazon über unsere Link abzuwickeln. Damit würdet ihr uns schon sehr helfen. Über Co-Autoren freuen wir uns aber noch mehr.