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.
Aufgabe
EinfachErstellen 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 einRefuelable-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 abstrakteVehicle-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 konkretegetBrand()-Methode hinzu, die die Marke zurückgibt, eine konkretegetFuelLevel()-Methode, die den aktuellen Kraftstoffstand zurückgibt, sowie eine abstraktedrive()-Methode, die von den Kindklassen implementiert werden muss.Car.php: Erstelle eineCar-Klasse, dieVehicleerweitert undRefuelableimplementiert. Binde beide erforderlichen Dateien am Anfang ein. Der Konstruktor akzeptiert die Marke und übergibt sie an den Konstruktor der übergeordneten Klasse. Implementieredrive(), sodass"[brand] car is driving"zurückgegeben wird. Implementiererefuel($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 einCar-Objekt. Gib das Ergebnis vondrive()in der ersten Zeile aus und anschließend das Ergebnis vonrefuel()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
?>Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienEinführung in OOPKlassen vs. ObjekteDas Schlüsselwort $thisMethodenEigenschaftenKonstruktor (__construct)Destruktor (__destruct)Rückblick – Einfacher Taschenrechner4Vererbung
Grundlegende VererbungDas Schlüsselwort parent::MethodenüberschreibungDas Schlüsselwort finalAbstrakte KlassenRückblick – Mitarbeiterhierarchie7Kapselung
Öffentlich, geschützt, privatZugriffsmodifikatoren im DetailGetter und SetterInformationsverbergungKonstruktor-Promotion (8.0)Wiederholung – System zur Verwaltung von Schülerdatensätzen2Namespaces & Autoloading
Einführung in NamespacesDas use-SchlüsselwortPSR-4-Autoloading-StandardComposer-AutoloaderRückblick – Organisiertes Projekt5Schnittstellen & Verträge
Einführung in SchnittstellenSchnittstellen implementierenMehrere Schnittstellen implementierenSchnittstelle vs. abstrakte KlasseTyp-Hinweise mit SchnittstellenRückblick – Formrechner8Magische Methoden
Einführung in magische Methoden__toString & __debugInfo__get, __set, __isset, __unset__call & __callStatic__clone & Objektklonen__serialize & __unserializeRückblick – Benutzerdefinierte Sammlung11Typsystem & Fehlerbehandlung
TypdeklarationenNullable-TypenUnion- & Intersection-TypenException-KlassenBenutzerdefinierte Exception-HierarchieTry, Catch, FinallyRückblick – Formularvalidator14Projekt: Bibliotheksverwaltung
ProjektübersichtBuch- und Benutzerklassen3Klassen-Eigenschaften
Instanz- vs. statische EigenschaftenKonstanten in KlassenStatische Methoden & EigenschaftenPrivate & geschützte EigenschaftenSchreibgeschützte Eigenschaften (PHP 8.1)Zusammenfassung – Bankkonto-Manager6Polymorphie
Methodenüberschreibung erneut betrachtetPolymorphie über SchnittstellenTyp-Hinweise & Union-TypenSpätes statisches BindenRückblick – Zahlungsabwickler9Traits
Einführung in TraitsMehrere Traits verwendenAuflösung von Trait-KonfliktenAbstrakte Methoden in TraitsTraits vs. Vererbung12Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFactory-MusterObserver-MusterStrategy-Muster15Abschließende Herausforderungen
E-Learning-PlattformBankingsystemEntwicklung einer SpielfigurFahrzeugvermietungÜbe selbstständig: Online-PHP-Compiler