Menu
Coddy logo textTech

Interface vs. Abstrakte Klasse

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

Nachdem Sie nun sowohl Interfaces als auch abstrakte Klassen kennengelernt haben, wollen wir klären, wann welches zu verwenden ist. Obwohl sie sich ähnlich sehen mögen, dienen sie unterschiedlichen Zwecken und haben unterschiedliche Fähigkeiten.

Merkmal Interface Abstrakte Klasse
Methoden Nur Methodensignaturen Sowohl abstrakte als auch konkrete Methoden
Eigenschaften Nur Konstanten Kann Eigenschaften haben
Mehrfachvererbung Eine Klasse kann mehrere implementieren Eine Klasse kann nur eine erweitern
Konstruktor Nicht erlaubt Kann einen Konstruktor haben

Verwenden Sie ein Interface, wenn Sie einen Vertrag definieren möchten, 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.

Verwenden Sie eine abstrakte Klasse, wenn Sie verwandte Klassen haben, die gemeinsamen Code teilen. Wenn Ihre Dog- und Cat-Klassen beide dieselbe eat()-Methodenimplementierung benötigen, ermöglicht es Ihnen eine abstrakte Animal-Klasse, diesen Code einmal zu schreiben.

Sie können 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ählen Sie Interfaces, um Funktionen über nicht verwandte Klassen hinweg zu definieren. Wählen Sie abstrakte Klassen, wenn Sie Code zwischen verwandten Klassen teilen müssen.

challenge icon

Aufgabe

Einfach

Lassen Sie uns ein Fahrzeugsystem bauen, das demonstriert, wann eine abstrakte Klasse gegenüber einem Interface verwendet werden sollte — und wie man beide Ansätze effektiv kombiniert.

Sie werden ein System erstellen, in dem Fahrzeuge gemeinsame Eigenschaften durch Vererbung teilen, aber auch zusätzliche Fähigkeiten durch Interfaces erhalten können. Dies spiegelt reale Design-Entscheidungen wider: Ein Auto "ist ein" Fahrzeug (Vererbung), aber es "hat auch die Fähigkeit", betankbar zu sein (Interface).

Organisieren Sie Ihren Code über vier Dateien:

  • Refuelable.php — Definieren Sie 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 — Definieren Sie 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 (beginnend bei 0) haben. Der Konstruktor akzeptiert den Markennamen. Fügen Sie eine konkrete getBrand() Methode hinzu, die die Marke zurückgibt, eine konkrete getFuelLevel() Methode, die den aktuellen Kraftstoffstand zurückgibt, und eine abstrakte drive() Methode, die Kindklassen implementieren müssen.
  • Car.php — Erstellen Sie eine Car Klasse, die Vehicle erweitert und Refuelable implementiert. Binden Sie beide erforderlichen Dateien oben ein. Der Konstruktor akzeptiert die Marke und übergibt sie an den Eltern-Konstruktor. Implementieren Sie drive(), um "[brand] car is driving" zurückzugeben. Implementieren Sie refuel($amount), um den Betrag zum Kraftstoffstand zu addieren und "[brand] refueled to [fuelLevel] liters" zurückzugeben.
  • main.php — Binden Sie die Car-Datei ein. Sie erhalten zwei Eingaben: einen Markennamen und eine Kraftstoffmenge (in einen Integer umwandeln). Erstellen Sie ein Car mit der Marke. Geben Sie das Ergebnis von drive() in der ersten Zeile aus und geben Sie dann das Ergebnis von refuel() mit der Kraftstoffmenge in der zweiten Zeile aus.

Diese Herausforderung veranschaulicht den entscheidenden 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 auf andere, nicht verwandte Klassen (wie einen Generator oder ein Boot) angewendet werden könnte, ohne dass diese Fahrzeuge sein müssen.

Spickzettel

Interfaces und abstrakte Klassen dienen unterschiedlichen Zwecken:

Merkmal Interface Abstrakte Klasse
Methoden Nur Methodensignaturen Sowohl abstrakte als auch konkrete Methoden
Eigenschaften Nur Konstanten Kann Eigenschaften haben
Mehrfachvererbung Eine Klasse kann viele implementieren Eine Klasse kann nur eine erweitern
Konstruktor Nicht erlaubt Kann einen Konstruktor haben

Verwenden Sie ein Interface, um einen Vertrag für nicht verwandte Klassen zu definieren. Verwenden Sie eine abstrakte Klasse, um gemeinsamen Code zwischen verwandten Klassen zu teilen.

Eine Klasse kann eine abstrakte Klasse erweitern und gleichzeitig mehrere Interfaces 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";
    }
}

Probier es selbst

<?php
require_once 'Car.php';

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

// TODO: Erstelle ein Auto mit der Marke

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

// TODO: Gib das Ergebnis von refuel() mit der Kraftstoffmenge 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