Wstrzykiwanie zależności
Część sekcji Programowanie obiektowe ścieżki PHP w Coddy. Lekcja 57 z 91.
W poprzedniej lekcji zobaczyłeś, jak kompozycja pozwala obiektowi Car zawierać obiekt Engine. Jednak tworzenie zależności wewnątrz klasy ma pewien problem — sprawia, że kod jest sztywny i trudny do testowania. Wstrzykiwanie zależności rozwiązuje ten problem, przekazując zależności z zewnątrz zamiast tworzyć je wewnątrz klasy.
Porównaj te dwa podejścia:
<?php
// Bez DI - zależność tworzona wewnątrz
class Car {
private Engine $engine;
public function __construct() {
$this->engine = new Engine(); // Ściśle powiązane
}
}
// Z DI - zależność przekazywana z zewnątrz
class Car {
public function __construct(private Engine $engine) {}
}
$engine = new Engine();
$car = new Car($engine); // Wstrzyknięte z zewnątrz
Drugie podejście to wstrzykiwanie zależności. Car nie tworzy własnego silnika — otrzymuje go. Ta prosta zmiana przynosi wiele korzyści: możesz przekazywać różne typy silników, podmieniać implementacje na potrzeby testów, a klasa staje się bardziej elastyczna.
Aby uzyskać jeszcze większą elastyczność, wstrzykuj interfejsy zamiast konkretnych klas:
<?php
interface EngineInterface {
public function start(): string;
}
class Car {
public function __construct(private EngineInterface $engine) {}
public function start(): string {
return $this->engine->start();
}
}
Teraz Car współpracuje z każdą klasą implementującą EngineInterface — silnikiem spalinowym, elektrycznym lub atrapą używaną do testów. To rozdzielenie zależności sprawia, że wstrzykiwanie zależności jest podstawą tworzenia łatwych w utrzymaniu i testowaniu aplikacji PHP.
Wyzwanie
ŁatwyStwórzmy system powiadomień, który pokazuje siłę wstrzykiwania zależności. Zamiast na sztywno określać sposób wysyłania wiadomości, stworzysz elastyczny system, w którym metodę dostarczania można łatwo zmienić.
Rozmieścisz kod w czterech plikach:
MessageSenderInterface.php: Zdefiniuj interfejs o nazwieMessageSenderInterfacez jedną metodąsend(string $recipient, string $message): string. Ten interfejs określa kontrakt, którego musi przestrzegać każdy nadawca wiadomości.EmailSender.php: Utwórz klasęEmailSender, która implementujeMessageSenderInterface. Dołącz plik interfejsu. Metodasend()powinna zwracać"Email to [recipient]: [message]".NotificationService.php: Utwórz klasęNotificationService, która otrzymuje swoją zależność przez konstruktor. Dołącz plik interfejsu. Klasa powinna:- Przyjmować
MessageSenderInterfacew konstruktorze, używając promocji właściwości konstruktora - Mieć metodę
notify(string $recipient, string $message), która deleguje działanie do wstrzykniętego nadawcy i zwraca wynik
NotificationServicezależy od interfejsu, a nie konkretnej klasy. Oznacza to, że możesz wstrzyknąć dowolnego nadawcę implementującego ten interfejs bez zmieniania usługi.- Przyjmować
main.php: Dołącz pliki EmailSender i NotificationService. Otrzymasz dwa dane wejściowe: odbiorcę i wiadomość. Utwórz obiektEmailSender, wstrzyknij go do obiektuNotificationService, a następnie wywołajnotify()i wypisz wynik.
Ten wzorzec zapewnia elastyczność NotificationService. Działa z dowolnym wstrzykniętym nadawcą, niezależnie od tego, czy jest to poczta e-mail, SMS, czy coś, czego jeszcze nie utworzyłeś. Usługa nie tworzy własnych zależności — otrzymuje je z zewnątrz.
Spróbuj swoich sił
<?php
require_once 'EmailSender.php';
require_once 'NotificationService.php';
// Odczytaj dane wejściowe
$recipient = trim(fgets(STDIN));
$message = trim(fgets(STDIN));
// TODO: Utwórz instancję EmailSender
// TODO: Przekaż ją do NotificationService
// TODO: Wywołaj notify() i wypisz wynik
?>Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania obiektowego
Pliki zewnętrzneWprowadzenie do programowania obiektowegoKlasy a obiektySłowo kluczowe $thisMetodyWłaściwościKonstruktor (__construct)Destruktor (__destruct)Powtórka — prosty kalkulator4Dziedziczenie
Podstawy dziedziczeniaSłowo kluczowe parent::Przesłanianie metodSłowo kluczowe finalKlasy abstrakcyjnePowtórzenie – hierarchia pracowników7Enkapsulacja
Public, protected, privateModyfikatory dostępu w szczegółachGettery i setteryUkrywanie informacjiPromocja parametrów konstruktora (8.0)Podsumowanie — system ewidencji studentów10Zaawansowane koncepcje OOP
Kompozycja a dziedziczenieWstrzykiwanie zależnościKlasy anonimoweEnumy (PHP 8.1)Włókna (PHP 8.1)Dogłębne omówienie klonowania obiektówGeneratory i iteratory2Przestrzenie nazw i autoloading
Wprowadzenie do przestrzeni nazwSłowo kluczowe useStandard autoloadingu PSR-4Autoloader ComposeraPodsumowanie – uporządkowany projekt5Interfejsy i kontrakty
Wprowadzenie do interfejsówImplementowanie interfejsówImplementowanie wielu interfejsówInterfejs a klasa abstrakcyjnaPodpowiadanie typów za pomocą interfejsówPodsumowanie — kalkulator kształtów8Metody magiczne
Wprowadzenie do metod magicznych__toString i __debugInfo__get, __set, __isset, __unset__call i __callStatic__clone i klonowanie obiektów__serialize i __unserializePodsumowanie — własna kolekcja11System typów i obsługa błędów
Deklaracje typówTypy nullableTypy sumy i przecięciaKlasy wyjątkówNiestandardowa hierarchia wyjątkówTry, Catch, FinallyPodsumowanie — walidator formularzy14Projekt: Zarządzanie biblioteką
Przegląd projektuKlasy książki i użytkownika3Właściwości klas
Właściwości instancji a właściwości statyczneStałe w klasachMetody i właściwości statyczneWłaściwości prywatne i chronioneWłaściwości tylko do odczytu (PHP 8.1)Podsumowanie — menedżer kont bankowych6Polimorfizm
Ponowne omówienie nadpisywania metodPolimorfizm za pomocą interfejsówPodpowiedzi typów i typy sumyPóźne wiązanie statycznePodsumowanie — procesor płatności9Cechy
Wprowadzenie do cechKorzystanie z wielu cechRozwiązywanie konfliktów cechMetody abstrakcyjne w cechachCechy a dziedziczenie12Wzorce projektowe, część 1
Wprowadzenie do wzorców projektowychWzorzec SingletonWzorzec FactoryWzorzec ObserverWzorzec StrategyPoćwicz samodzielnie: Kompilator PHP online