Przekazywanie zależności
Argumenty, lub „zależności“ w terminologii DI, mogą być przekazywane do klas na następujące główne sposoby:
- przekazywanie przez konstruktora
- przekazywanie przez metodę (zwane setterem)
- poprzez ustawienie zmiennej
- przez metodę, adnotację lub atrybut inject.
Zilustrujemy teraz różne warianty na konkretnych przykładach.
Przekazywanie przez konstruktora
Zależności są przekazywane jako argumenty do konstruktora w czasie tworzenia obiektu:
Ta forma jest odpowiednia dla obowiązkowych zależności, które klasa bezwzględnie potrzebuje do funkcjonowania, ponieważ bez nich nie można utworzyć instancji.
Od PHP 8.0 możemy używać krótszej formy notacji (constructor property promotion), która jest funkcjonalnie równoważna:
Od PHP 8.1 zmienna może być oznaczona flagą readonly
, która deklaruje, że zawartość zmiennej nie będzie
się już zmieniać:
Kontener DI przekazuje zależność do konstruktora automatycznie przez autowiring. Argumenty, które nie mogą być przekazane w ten sposób (np. ciągi znaków, liczby, booleans) są zapisywane w konfiguracji.
Piekło konstruktorów
Termin constructor hell odnosi się do sytuacji, w której dziecko dziedziczy po klasie rodzica, której konstruktor wymaga zależności, a dziecko również wymaga zależności. Musi ono również przejąć i przekazać zależności rodzica:
Problem pojawia się, gdy chcemy zmienić konstruktor klasy BaseClass
, na przykład gdy dodamy nową zależność.
Wtedy musimy zmodyfikować również wszystkie konstruktory dzieci. Co czyni taką modyfikację piekłem.
Jak temu zapobiec? Rozwiązaniem jest przedkładanie kompozycji nad dziedziczenie.
Zaprojektujmy więc kod inaczej. Unikniemy abstrakcyjnych klas
Base*
. Zamiast MyClass
uzyskiwać pewną funkcjonalność poprzez dziedziczenie z
BaseClass
, będzie mieć tę funkcjonalność przekazaną jako zależność:
Przekazywanie przez setera
Zależności są przekazywane przez wywołanie metody, która przechowuje je w prywatnej właściwości. Zwykła konwencja
nazewnicza dla tych metod ma postać set*()
, dlatego są one nazywane setterami, ale oczywiście mogą być nazywane
inaczej.
Ta metoda jest przydatna dla opcjonalnych zależności, które nie są konieczne dla funkcji klasy, ponieważ nie jest gwarantowane, że obiekt faktycznie otrzyma zależność (tj. Że użytkownik wywoła metodę).
Jednocześnie metoda ta pozwala na wielokrotne wywoływanie setera w celu zmiany zależności. Jeśli nie jest to pożądane,
dodaj do metody kontrolę lub od PHP 8.1 oznacz zmienną $cache
flagą readonly
.
Wywołanie setera jest zdefiniowane w konfiguracji kontenera DI w sekcji setup. Również w tym przypadku wykorzystywane jest automatyczne przekazywanie zależności przez autowiring:
Poprzez ustawienie zmiennej
Zależności są przekazywane przez zapis bezpośrednio do zmiennej członkowskiej:
Metoda ta jest uważana za niewłaściwą, ponieważ zmienna członkowska musi być zadeklarowana jako public
. Tym
samym nie mamy kontroli nad tym, czy przekazana zależność jest rzeczywiście danego typu (tak było przed PHP 7.4) i tracimy
możliwość reagowania na nowo przypisaną zależność własnym kodem, na przykład w celu zapobieżenia późniejszej zmianie.
W tym samym czasie zmienna staje się częścią publicznego interfejsu klasy, co może nie być pożądane.
Ustawienia zmiennej definiujemy w konfiguracji kontenera DI w sekcji setup:
Inject
O ile poprzednie trzy metody obowiązują ogólnie we wszystkich językach obiektowych, o tyle wstrzykiwanie za pomocą metody, adnotacji lub atrybutu inject jest specyficzne dla prezenterów Nette. Zostały one omówione w osobnym rozdziale.
Jaką metodę wybrać?
- Konstruktor jest odpowiedni dla obowiązkowych zależności, które klasa potrzebuje do działania
- setter jest odpowiedni dla opcjonalnych zależności lub zależności, które mogą być zmienione
- zmienne publiczne nie są odpowiednie