Domande frequenti su DI (FAQ)

DI è un altro nome per IoC?

Inversion of Control (IoC) è un principio che si concentra sul modo in cui il codice viene eseguito: sia che il codice inizi il codice esterno, sia che il codice sia integrato nel codice esterno, che poi lo chiama. L'IoC è un concetto ampio che include gli eventi, il cosiddetto principio di Hollywood e altri aspetti. Anche le fabbriche, che fanno parte della regola #3: Let the Factory Handle It, e che rappresentano l'inversione dell'operatore new, sono componenti di questo concetto.

La Dependency Injection (DI) riguarda il modo in cui un oggetto conosce un altro oggetto, cioè la dipendenza. È un modello di progettazione che richiede il passaggio esplicito delle dipendenze tra gli oggetti.

Pertanto, si può dire che DI sia una forma specifica di IoC. Tuttavia, non tutte le forme di IoC sono adatte in termini di purezza del codice. Per esempio, tra gli anti-pattern, includiamo tutte le tecniche che lavorano con lo stato globale o il cosiddetto Service Locator.

Che cos'è un Service Locator?

Un localizzatore di servizi è un'alternativa alla Dependency Injection. Funziona creando un archivio centrale in cui sono registrati tutti i servizi o le dipendenze disponibili. Quando un oggetto ha bisogno di una dipendenza, la richiede al Service Locator.

Tuttavia, rispetto alla Dependency Injection, perde in trasparenza: le dipendenze non vengono passate direttamente agli oggetti e non sono quindi facilmente identificabili, il che richiede l'esame del codice per scoprire e comprendere tutte le connessioni. Anche i test sono più complicati, perché non si possono semplicemente passare gli oggetti mock agli oggetti testati, ma bisogna passare attraverso il Service Locator. Inoltre, il Service Locator sconvolge la progettazione del codice, poiché i singoli oggetti devono essere a conoscenza della sua esistenza, a differenza della Dependency Injection, in cui gli oggetti non sono a conoscenza del contenitore DI.

Quando è meglio non usare DI?

Non ci sono difficoltà note associate all'uso del design pattern Dependency Injection. Al contrario, ottenere le dipendenze da posizioni accessibili a livello globale comporta una serie di complicazioni, così come l'uso di un Service Locator. Pertanto, è consigliabile utilizzare sempre la DI. Non si tratta di un approccio dogmatico, ma semplicemente non è stata trovata un'alternativa migliore.

Tuttavia, ci sono alcune situazioni in cui non è possibile passare gli oggetti tra loro e ottenerli dallo spazio globale. Ad esempio, quando si esegue il debug del codice e si ha bisogno di scaricare il valore di una variabile in un punto specifico del programma, di misurare la durata di una certa parte del programma o di registrare un messaggio. In questi casi, quando si tratta di azioni temporanee che verranno successivamente rimosse dal codice, è legittimo utilizzare un dumper, un cronometro o un logger accessibile a livello globale. Questi strumenti, dopo tutto, non appartengono alla progettazione del codice.

L'uso di DI ha degli svantaggi?

L'uso della Dependency Injection comporta qualche svantaggio, come una maggiore complessità di scrittura del codice o prestazioni peggiori? Che cosa perdiamo quando iniziamo a scrivere codice secondo la DI?

DI non ha alcun impatto sulle prestazioni dell'applicazione o sui requisiti di memoria. Le prestazioni del contenitore DI possono avere un ruolo, ma nel caso di Nette DI, il contenitore è compilato in PHP puro, quindi il suo overhead durante l'esecuzione dell'applicazione è sostanzialmente nullo.

Quando si scrive il codice, è necessario creare costruttori che accettino le dipendenze. In passato, questo poteva richiedere molto tempo, ma grazie ai moderni IDE e alla promozione delle proprietà dei costruttori, ora è una questione di pochi secondi. Le fabbriche possono essere facilmente generate utilizzando Nette DI e un plugin di PhpStorm con pochi clic. D'altra parte, non è necessario scrivere singleton e punti di accesso statici.

Si può concludere che un'applicazione progettata correttamente utilizzando DI non è né più corta né più lunga rispetto a un'applicazione che utilizza singleton. Le parti di codice che lavorano con le dipendenze vengono semplicemente estratte dalle singole classi e spostate in nuove posizioni, cioè nel contenitore DI e nelle fabbriche.

Come riscrivere un'applicazione legacy in DI?

La migrazione da un'applicazione legacy alla Dependency Injection può essere un processo impegnativo, soprattutto per applicazioni grandi e complesse. È importante affrontare questo processo in modo sistematico.

  • Quando si passa alla Dependency Injection, è importante che tutti i membri del team comprendano i principi e le pratiche da utilizzare.
  • In primo luogo, è necessario eseguire un'analisi dell'applicazione esistente per identificare i componenti chiave e le loro dipendenze. Creare un piano per le parti da rifattorizzare e in quale ordine.
  • Implementare un contenitore DI o, meglio ancora, utilizzare una libreria esistente come Nette DI.
  • Rifattorizzare gradualmente ogni parte dell'applicazione per utilizzare la Dependency Injection. Ciò può comportare la modifica di costruttori o metodi per accettare le dipendenze come parametri.
  • Modificare i punti del codice in cui vengono creati gli oggetti di dipendenza, in modo che le dipendenze siano invece iniettate dal contenitore. Questo può includere l'uso di fabbriche.

Ricordate che il passaggio alla Dependency Injection è un investimento nella qualità del codice e nella sostenibilità a lungo termine dell'applicazione. Sebbene possa essere impegnativo apportare queste modifiche, il risultato dovrebbe essere un codice più pulito, modulare e facilmente testabile, pronto per estensioni e manutenzioni future.

Perché la composizione è preferibile all'ereditarietà?

È preferibile usare la composizione invece dell'ereditarietà perché serve a riutilizzare il codice senza doversi preoccupare delle conseguenze delle modifiche. In questo modo, si ottiene un accoppiamento meno rigido, in cui non ci si deve preoccupare che la modifica di un codice comporti la necessità di modificare altro codice dipendente. Un esempio tipico è la situazione denominata " inferno dei costruttori".

Nette DI Container può essere utilizzato al di fuori di Nette?

Assolutamente sì. Nette DI Container fa parte di Nette, ma è stato progettato come una libreria autonoma che può essere utilizzata indipendentemente da altre parti del framework. È sufficiente installarla con Composer, creare un file di configurazione che definisca i servizi e poi utilizzare poche righe di codice PHP per creare il contenitore DI. In questo modo si può iniziare a sfruttare immediatamente la Dependency Injection nei propri progetti.

Il capitolo Nette DI Container descrive un caso d'uso specifico, compreso il codice.

Perché la configurazione è nei file NEON?

NEON è un linguaggio di configurazione semplice e facilmente leggibile sviluppato all'interno di Nette per la configurazione di applicazioni, servizi e relative dipendenze. Rispetto a JSON o YAML, offre opzioni molto più intuitive e flessibili. In NEON, è possibile descrivere in modo naturale legami che non sarebbe possibile scrivere in Symfony e YAML o solo attraverso una descrizione complessa.

L'analisi dei file NEON rallenta l'applicazione?

Sebbene l'analisi dei file NEON sia molto rapida, questo aspetto non ha molta importanza. Il motivo è che l'analisi dei file avviene solo una volta durante il primo avvio dell'applicazione. In seguito, il codice del contenitore DI viene generato, memorizzato sul disco ed eseguito per ogni richiesta successiva, senza bisogno di ulteriori analisi.

Questo è il modo in cui funziona in un ambiente di produzione. Durante lo sviluppo, i file NEON vengono analizzati ogni volta che il loro contenuto cambia, assicurando che lo sviluppatore abbia sempre un contenitore DI aggiornato. Come già detto, il parsing vero e proprio è questione di un istante.

Come si accede ai parametri del file di configurazione nella propria classe?

Tenete a mente la regola n. 1: lasciate che vi venga passato. Se una classe richiede informazioni da un file di configurazione, non è necessario capire come accedere a tali informazioni, ma è sufficiente richiederle, ad esempio attraverso il costruttore della classe. Ed eseguiamo il passaggio nel file di configurazione.

In questo esempio, %myParameter% è un segnaposto per il valore del parametro myParameter, che sarà passato al costruttore MyClass:

# config.neon
parameters:
	myParameter: Some value

services:
	- MyClass(%myParameter%)

Se si desidera passare più parametri o utilizzare il cablaggio automatico, è utile avvolgere i parametri in un oggetto.

Nette supporta l'interfaccia PSR-11 Container?

Nette DI Container non supporta direttamente PSR-11. Tuttavia, se è necessaria l'interoperabilità tra Nette DI Container e librerie o framework che si aspettano l'interfaccia PSR-11 Container, è possibile creare un semplice adattatore che funga da ponte tra Nette DI Container e PSR-11.

versione: 3.x