Menu
Coddy logo textTech

Schnittstelle vs. abstrakte Klasse

Teil des Abschnitts Objektorientierte Programmierung der PHP-Journey von Coddy. Lektion 30 von 91.

Nachdem du nun sowohl Schnittstellen als auch abstrakte Klassen kennengelernt hast, klären wir, wann du welche verwenden solltest. Obwohl sie ähnlich erscheinen mögen, dienen sie unterschiedlichen Zwecken und verfügen über unterschiedliche Möglichkeiten.

Merkmal Schnittstelle Abstrakte Klasse
Methoden Nur Methodensignaturen Sowohl abstrakte als auch konkrete Methoden
Eigenschaften Nur Konstanten Kann Eigenschaften enthalten
Mehrfachvererbung Eine Klasse kann viele implementieren Eine Klasse kann nur eine erweitern
Konstruktor Kann eine __construct-Signatur deklarieren (kein Rumpf) Kann einen Konstruktor mit einem Rumpf definieren

Verwende ein Interface, wenn du einen Vertrag definieren möchtest, den nicht verwandte Klassen erfüllen können. Beispielsweise könnten sowohl ein Printer als auch ein Logger ein Writable-Interface implementieren, obwohl sie keinen gemeinsamen Vorfahren haben.

Verwende eine abstrakte Klasse, wenn du verwandte Klassen hast, die gemeinsamen Code nutzen. Wenn deine Klassen Dog und Cat beide dieselbe Implementierung der Methode eat() benötigen, kannst du mit einer abstrakten Klasse Animal diesen Code einmal schreiben.

Du kannst auch beide Ansätze kombinieren. Eine Klasse kann eine abstrakte Klasse erweitern und gleichzeitig mehrere Schnittstellen implementieren:

<?php
abstract class Animal {
    protected $name;
    
    public function eat() {
        return $this->name . " is eating";
    }
}

interface Swimmable {
    public function swim();
}

class Duck extends Animal implements Swimmable {
    public function __construct($name) {
        $this->name = $name;
    }
    
    public function swim() {
        return $this->name . " is swimming";
    }
}

Wichtiger Punkt: Wähle Interfaces, um Fähigkeiten über nicht verwandte Klassen hinweg zu definieren. Wähle abstrakte Klassen, wenn du Code unter verwandten Klassen gemeinsam nutzen musst.

challenge icon

Aufgabe

Einfach

Erstellen wir ein Fahrzeugsystem, das veranschaulicht, wann eine abstrakte Klasse und wann ein Interface verwendet werden sollte und wie sich beide Ansätze effektiv kombinieren lassen.

Du erstellst ein System, in dem Fahrzeuge gemeinsame Eigenschaften durch Vererbung teilen, aber durch Interfaces auch zusätzliche Fähigkeiten erhalten können. Dies spiegelt Entscheidungen aus der Praxis wider: Ein Auto „ist ein“ Fahrzeug (Vererbung), aber es „hat die Fähigkeit“, betankbar zu sein (Interface).

Organisiere deinen Code auf vier Dateien verteilt:

  • Refuelable.php: Definiere ein Refuelable-Interface mit einer einzigen Methodensignatur: refuel($amount). Diese Fähigkeit kann auf jede Klasse angewendet werden, die betankt werden muss, unabhängig von ihrer Vererbungshierarchie.
  • Vehicle.php: Definiere eine abstrakte Vehicle-Klasse, die gemeinsame Funktionalität für alle Fahrzeuge bereitstellt. Sie sollte eine geschützte $brand-Eigenschaft und eine geschützte $fuelLevel-Eigenschaft (mit einem Anfangswert von 0) besitzen. Der Konstruktor akzeptiert den Markennamen. Füge eine konkrete getBrand()-Methode hinzu, die die Marke zurückgibt, eine konkrete getFuelLevel()-Methode, die den aktuellen Kraftstoffstand zurückgibt, sowie eine abstrakte drive()-Methode, die von den Kindklassen implementiert werden muss.
  • Car.php: Erstelle eine Car-Klasse, die Vehicle erweitert und Refuelable implementiert. Binde beide erforderlichen Dateien am Anfang ein. Der Konstruktor akzeptiert die Marke und übergibt sie an den Konstruktor der übergeordneten Klasse. Implementiere drive(), sodass "[brand] car is driving" zurückgegeben wird. Implementiere refuel($amount), sodass der Betrag zum Kraftstoffstand addiert und "[brand] refueled to [fuelLevel] liters" zurückgegeben wird.
  • main.php: Binde die Car-Datei ein. Du erhältst zwei Eingaben: einen Markennamen und eine Kraftstoffmenge (in eine Ganzzahl umwandeln). Erstelle mit der Marke ein Car-Objekt. Gib das Ergebnis von drive() in der ersten Zeile aus und anschließend das Ergebnis von refuel() mit der Kraftstoffmenge in der zweiten Zeile.

Diese Aufgabe veranschaulicht den wesentlichen Unterschied: Die abstrakte Vehicle-Klasse teilt Code und stellt eine „ist-ein“-Beziehung her, während das Refuelable-Interface eine Fähigkeit hinzufügt, die auch auf andere, nicht verwandte Klassen (wie einen Generator oder ein Boot) angewendet werden könnte, ohne dass diese Fahrzeuge sein müssen.

Probier es selbst

<?php
require_once 'Car.php';

// Eingabe lesen
$brand = trim(fgets(STDIN));
$fuelAmount = intval(trim(fgets(STDIN)));

// TODO: Erstelle ein Car mit der brand

// TODO: Gib das Ergebnis von drive() in der ersten Zeile aus

// TODO: Gib das Ergebnis von refuel() mit dem fuel amount in der zweiten Zeile aus
?>
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Online-PHP-Compiler