Dependency Injection
Teil des Abschnitts Objektorientierte Programmierung der PHP-Journey von Coddy — Lektion 57 von 91.
In der vorherigen Lektion haben Sie gesehen, wie Komposition es ermöglicht, dass ein Car ein Engine-Objekt enthält. Aber es gibt ein Problem beim Erstellen von Abhängigkeiten innerhalb der Klasse – es macht den Code starr und schwer zu testen. Dependency Injection löst dies, indem Abhängigkeiten von außen übergeben werden, anstatt sie intern zu erstellen.
Vergleichen Sie diese beiden Ansätze:
<?php
// Ohne DI - Abhängigkeit wird intern erstellt
class Car {
private Engine $engine;
public function __construct() {
$this->engine = new Engine(); // Fest gekoppelt
}
}
// Mit DI - Abhängigkeit wird übergeben
class Car {
public function __construct(private Engine $engine) {}
}
$engine = new Engine();
$car = new Car($engine); // Von außen injiziert
Der zweite Ansatz ist Dependency Injection. Die Klasse Car erstellt ihren Motor nicht selbst – sie erhält einen. Diese einfache Änderung hat enorme Vorteile: Sie können verschiedene Motortypen übergeben, Implementierungen für Tests austauschen und die Klasse wird flexibler.
Für noch mehr Flexibilität injizieren Sie Interfaces anstelle von konkreten Klassen:
<?php
interface EngineInterface {
public function start(): string;
}
class Car {
public function __construct(private EngineInterface $engine) {}
public function start(): string {
return $this->engine->start();
}
}
Jetzt funktioniert Car mit jeder Klasse, die EngineInterface implementiert – einem Benzinmotor, einem Elektromotor oder einem Mock zum Testen. Diese Entkopplung ist der Grund, warum Dependency Injection grundlegend für das Schreiben von wartbaren, testbaren PHP-Anwendungen ist.
Aufgabe
EinfachLassen Sie uns ein Benachrichtigungssystem bauen, das die Vorteile von Dependency Injection demonstriert. Anstatt fest zu kodieren, wie Nachrichten gesendet werden, erstellen Sie ein flexibles System, bei dem die Zustellungsmethode einfach ausgetauscht werden kann.
Sie werden Ihren Code über vier Dateien organisieren:
MessageSenderInterface.php— Definieren Sie ein Interface namensMessageSenderInterfacemit einer einzigen Methodesend(string $recipient, string $message): string. Dieses Interface legt den Vertrag fest, dem jeder Nachrichtensender folgen muss.EmailSender.php— Erstellen Sie eine KlasseEmailSender, dieMessageSenderInterfaceimplementiert. Binden Sie die Interface-Datei ein. Die Methodesend()sollte"Email to $recipient: $message"zurückgeben.NotificationService.php— Erstellen Sie eine KlasseNotificationService, die ihre Abhängigkeit über den Konstruktor erhält. Binden Sie die Interface-Datei ein. Die Klasse sollte:- Ein
MessageSenderInterfacein ihrem Konstruktor mittels Constructor Promotion akzeptieren - Eine Methode
notify(string $recipient, string $message)haben, die den Aufruf an den injizierten Sender delegiert und dessen Ergebnis zurückgibt
NotificationServicevom Interface abhängt und nicht von einer konkreten Klasse. Das bedeutet, dass Sie jeden Sender injizieren könnten, der das Interface implementiert, ohne den Service zu ändern.- Ein
main.php— Binden Sie die Dateien EmailSender und NotificationService ein. Sie erhalten zwei Eingaben: einen Empfänger und eine Nachricht. Erstellen Sie einenEmailSender, injizieren Sie ihn in einenNotificationService, rufen Sie dannnotify()auf und geben Sie das Ergebnis aus.
Dieses Muster hält Ihren NotificationService flexibel — er funktioniert mit jedem Sender, den Sie injizieren, egal ob es sich um E-Mail, SMS oder etwas handelt, das Sie noch nicht gebaut haben. Der Service erstellt seine Abhängigkeiten nicht selbst; er erhält sie von außen.
Spickzettel
Dependency Injection (DI) ist eine Technik, bei der Abhängigkeiten von außen in eine Klasse übergeben werden, anstatt intern erstellt zu werden.
Ohne DI (stark gekoppelt):
<?php
class Car {
private Engine $engine;
public function __construct() {
$this->engine = new Engine(); // Intern erstellt
}
}Mit DI (lose gekoppelt):
<?php
class Car {
public function __construct(private Engine $engine) {}
}
$engine = new Engine();
$car = new Car($engine); // Von außen injiziertFür maximale Flexibilität sollten Interfaces anstelle von konkreten Klassen injiziert werden:
<?php
interface EngineInterface {
public function start(): string;
}
class Car {
public function __construct(private EngineInterface $engine) {}
public function start(): string {
return $this->engine->start();
}
}Dies ermöglicht es Car, mit jeder Klasse zu arbeiten, die EngineInterface implementiert, wodurch der Code wartbarer und testbarer wird.
Probier es selbst
<?php
require_once 'EmailSender.php';
require_once 'NotificationService.php';
// Eingabe lesen
$recipient = trim(fgets(STDIN));
$message = trim(fgets(STDIN));
// TODO: Eine EmailSender-Instanz erstellen
// TODO: In einen NotificationService injizieren
// TODO: notify() aufrufen und das Ergebnis ausgeben
?>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 die OOPKlassen vs. ObjekteDas $this-SchlüsselwortMethodenEigenschaftenKonstruktor (__construct)Destruktor (__destruct)Rückblick - Einfacher Taschenrechner4Vererbung
Grundlagen der VererbungDas parent:: SchlüsselwortMethoden überschreibenDas final SchlüsselwortAbstrakte KlassenZusammenfassung – Mitarbeiter-Hierarchie7Kapselung
Public, Protected, PrivateZugriffsmodifikatoren im DetailGetter und SetterInformation HidingConstructor Promotion (8.0)Zusammenfassung – Studentenverwaltungssystem10Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungDependency InjectionAnonyme KlassenEnums (PHP 8.1)Fibers (PHP 8.1)Objekt-Klonen im DetailGeneratoren & Iteratoren2Namensräume & Autoloading
Einführung in NamensräumeDas use-SchlüsselwortPSR-4 Autoloading-StandardComposer AutoloaderZusammenfassung – Organisiertes Projekt5Interfaces & Verträge
Einführung in InterfacesInterfaces implementierenMehrfache Interface-ImplementierungInterface vs. Abstrakte KlasseType Hinting mit InterfacesZusammenfassung – Formen-Rechner8Magische Methoden
Einführung in magische Methoden__toString & __debugInfo__get, __set, __isset, __unset__call & __callStatic__clone & Objekt-Klonen__serialize & __unserializeZusammenfassung - Eigene Collection11Typsystem & Fehlerbehandlung
TypdeklarationenNullable-TypenUnion- & Intersection-TypenException-KlassenEigene Exception-HierarchieTry, Catch, FinallyZusammenfassung - Formular-Validator14Projekt: Bibliotheksverwaltung
ProjektübersichtBuch- und Benutzerklassen3Klasseneigenschaften
Instanz- vs. statische EigenschaftenKonstanten in KlassenStatische Methoden & EigenschaftenPrivate & geschützte EigenschaftenReadonly-Eigenschaften (PHP 8.1)Zusammenfassung – Bankkonto-Manager6Polymorphismus
Method Overriding erneut betrachtetPolymorphismus über InterfacesType Hinting & Union TypesLate Static BindingZusammenfassung – Payment Processor9Traits
Einführung in TraitsVerwendung mehrerer TraitsKonfliktlösung bei TraitsAbstrakte Methoden in TraitsTraits vs. Vererbung12Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFactory-MusterObserver-MusterStrategy-Muster15Abschlussherausforderungen
E-Learning-PlattformBanksystemSpielcharakter-EntwicklungFahrzeugvermietung