Passaggio delle dipendenze
Gli argomenti, o nella terminologia DI „dipendenze“, possono essere passati alle classi nei seguenti modi principali:
- passaggio tramite costruttore
- passaggio tramite metodo (cosiddetto setter)
- impostazione di una variabile
- tramite metodo, annotazione o attributo inject
Ora mostreremo le singole varianti con esempi concreti.
Passaggio tramite costruttore
Le dipendenze vengono passate al momento della creazione dell'oggetto come argomenti del costruttore:
Questa forma è adatta per le dipendenze obbligatorie di cui la classe ha necessariamente bisogno per funzionare, poiché senza di esse non sarà possibile creare l'istanza.
Da PHP 8.0 possiamo usare una forma di scrittura più breve (constructor property promotion), che è funzionalmente equivalente:
Da PHP 8.1 è possibile contrassegnare la variabile con il flag readonly
, che dichiara che il contenuto della
variabile non cambierà più:
Il container DI passa automaticamente le dipendenze al costruttore tramite autowiring. Gli argomenti che non possono essere passati in questo modo (es. stringhe, numeri, booleani) li scriviamo nella configurazione.
Constructor hell
Il termine constructor hell indica la situazione in cui un figlio eredita da una classe genitore il cui costruttore richiede dipendenze, e allo stesso tempo il figlio richiede dipendenze. Deve quindi ricevere e passare anche quelle del genitore:
Il problema sorge nel momento in cui vogliamo modificare il costruttore della classe BaseClass
, ad esempio quando
viene aggiunta una nuova dipendenza. Allora è necessario modificare anche tutti i costruttori dei figli. Il che rende una tale
modifica un inferno.
Come prevenirlo? La soluzione è dare la preferenza alla composizione rispetto all'ereditarietà.
Quindi progetteremo il codice diversamente. Eviteremo le classi astratte Base*
.
Invece che MyClass
ottenga una certa funzionalità ereditando da BaseClass
, si farà passare questa
funzionalità come dipendenza:
Passaggio tramite setter
Le dipendenze vengono passate chiamando un metodo che le salva in una variabile privata. La convenzione usuale per nominare
questi metodi è la forma set*()
, per questo vengono chiamati setter, ma possono ovviamente chiamarsi in qualsiasi
altro modo.
Questo metodo è adatto per le dipendenze opzionali che non sono indispensabili per la funzione della classe, poiché non è garantito che l'oggetto riceva effettivamente la dipendenza (cioè che l'utente chiami il metodo).
Allo stesso tempo, questo metodo consente di chiamare il setter ripetutamente e quindi modificare la dipendenza. Se ciò non è
desiderato, aggiungiamo un controllo nel metodo, o da PHP 8.1 contrassegniamo la property $cache
con il flag
readonly
.
La chiamata del setter viene definita nella configurazione del container DI nella chiave setup. Anche qui si utilizza il passaggio automatico delle dipendenze tramite autowiring:
Impostazione di una variabile
Le dipendenze vengono passate scrivendo direttamente nella variabile membro:
Questo metodo è considerato inappropriato, poiché la variabile membro deve essere dichiarata come public
. E
quindi non abbiamo controllo sul fatto che la dipendenza passata sia effettivamente del tipo specificato (valido prima di PHP 7.4)
e perdiamo la possibilità di reagire alla dipendenza appena assegnata con codice personalizzato, ad esempio impedendo modifiche
successive. Allo stesso tempo, la variabile diventa parte dell'interfaccia pubblica della classe, il che potrebbe non essere
desiderabile.
L'impostazione della variabile viene definita nella configurazione del container DI nella sezione setup:
Inject
Mentre i tre metodi precedenti valgono in generale in tutti i linguaggi orientati agli oggetti, l'iniezione tramite metodo, annotazione o attributo inject è specifica esclusivamente per i presenter in Nette. Ne parla un capitolo separato.
Quale metodo scegliere?
- il costruttore è adatto per le dipendenze obbligatorie di cui la classe ha necessariamente bisogno per funzionare
- il setter è invece adatto per le dipendenze opzionali, o dipendenze che si desidera poter modificare ulteriormente
- le variabili pubbliche non sono adatte