modulo 09 di 24 / Ore 21-40
Moduli 9-16 / Checkpoint 40 ore
Inversione del controllo senza framework
Capire l'inversione del controllo in modo pratico: il caso d'uso non sceglie le implementazioni, le riceve dall'esterno.
Inversione del controllo senza framework
Inversione del controllo sembra un termine grande.
Nel nostro laboratorio significa una cosa semplice:
Il caso d'uso non decide quale archivio usare.
Qualcun altro glielo passa.
Non serve un framework.
Non serve un container.
Serve solo rendere esplicito dove vengono create e collegate le dipendenze.
Obiettivo
Alla fine devi saper spiegare inversione del controllo con un esempio del Registro del laboratorio.
Devi anche saper riconoscere:
- chi decide l’implementazione concreta;
- chi riceve una dipendenza;
- perche questo rende il codice piu testabile e modificabile.
Controllo normale
Forma rigida:
export function registraPrestito(comando) {
const archivioStrumenti = creaArchivioStrumentiLocalStorage();
const archivioPrestiti = creaArchivioPrestitiLocalStorage();
// usa gli archivi
}
Qui il caso d’uso controlla tutto:
- sceglie l’archivio;
- lo crea;
- lo usa.
Se vogliamo cambiare archivio, tocchiamo il caso d’uso.
Controllo invertito
Forma flessibile:
export function creaRegistraPrestito({
archivioStrumenti,
archivioPrestiti,
}) {
return function registraPrestito(comando) {
// usa gli archivi ricevuti
};
}
Qui il caso d’uso non crea gli archivi.
Li riceve.
Il controllo sulla scelta dell’implementazione si sposta fuori.
Questa e l’inversione.
Dove va il controllo
Il controllo va nel punto di composizione.
Esempio:
const archivioStrumenti = creaArchivioStrumentiInMemoria();
const archivioPrestiti = creaArchivioPrestitiInMemoria();
const registraPrestito = creaRegistraPrestito({
archivioStrumenti,
archivioPrestiti,
});
Se domani cambiamo archivio:
const archivioStrumenti = creaArchivioStrumentiLocalStorage();
const archivioPrestiti = creaArchivioPrestitiLocalStorage();
Il caso d’uso resta uguale.
Non serve un framework
Molti framework hanno strumenti per la dependency injection.
In questo corso non ci servono.
Useremo funzioni e oggetti:
const casoUso = creaCasoUso({
dipendenzaUno,
dipendenzaDue,
});
Questa forma e leggibile, esplicita e sufficiente.
Prima capire il concetto.
Poi, se un giorno userai framework, saprai cosa stanno automatizzando.
Benefici pratici
Inversione del controllo ci permette di:
- cambiare archivio;
- testare con fake;
- evitare import concreti nei casi d’uso;
- rendere visibili le dipendenze;
- ridurre accoppiamento tra Application e Infrastructure.
Non e teoria astratta.
Serve quando il progetto deve cambiare.
Rischio: farne troppo
Non trasformare ogni funzione in una fabbrica complicata.
Esempio eccessivo:
creaCreaCreaVerificaNome({ normalizzatore, validatore, configurazione })
Per un laboratorio scolastico sarebbe rumore.
Usa inversione del controllo dove c’e un motivo concreto:
- archivio sostituibile;
- generatore di id controllabile;
- data controllabile nei test;
- servizio esterno.
Esempio con generatore di id
In alcuni casi possiamo passare anche un generatore di id:
export function creaRegistraStrumento({
archivioStrumenti,
generaId,
}) {
return function registraStrumento(comando) {
const strumento = creaStrumento({
...comando,
id: comando.id ?? generaId(),
});
archivioStrumenti.salva(strumento);
return strumento;
};
}
Nei test:
const generaId = () => "s-test-001";
Il test diventa prevedibile.
Questo e un uso sensato dell’inversione del controllo.
Esercizio
Rispondi:
Chi sceglie l'archivio in questa versione?
Codice A:
export function registraStrumento(comando) {
const archivio = creaArchivioStrumentiLocalStorage();
}
Codice B:
export function creaRegistraStrumento({ archivioStrumenti }) {
return function registraStrumento(comando) {
archivioStrumenti.salva(comando);
};
}
Risposta:
A: sceglie il caso d'uso.
B: sceglie il codice che crea il caso d'uso.
Scheda
Compila:
# Inversione del controllo
Caso d'uso:
Dipendenze ricevute:
- ...
Dipendenze che prima erano create dentro:
- ...
Punto in cui vengono create ora:
Vantaggio ottenuto:
Rischio di complicare troppo:
Cosa produrre
Alla fine della lezione devi avere:
- una spiegazione breve di inversione del controllo;
- almeno un caso d’uso che riceve archivi;
- punto di composizione che crea archivi e casi d’uso;
- esempio di sostituzione archivio;
- nota su cosa non conviene invertire per ora.
Spiegazione guidata per studiare
Questa lezione va studiata su due piani. Il primo è il comportamento visibile del Registro del laboratorio: che cosa vede o fa l’utente quando registra uno strumento, cerca un dispositivo, apre un prestito o segnala un guasto. Il secondo è il confine architetturale: dove deve stare il codice che rende possibile quel comportamento.
Per inversione del controllo senza framework, il punto centrale è questo: Un caso d’uso coordina un’operazione completa: riceve un comando, legge ciò che serve, applica regole, salva il risultato e restituisce un esito.
Il rischio tipico è far diventare il caso d’uso una seconda UI o un secondo database. Deve coordinare, non mostrare HTML e non conoscere dettagli tecnici di salvataggio. Per evitarlo, non partire dal file che hai già aperto nell’editor. Parti dalla domanda: “quale responsabilità sto modificando?”. Se la risposta riguarda una regola del laboratorio, guarda il dominio. Se riguarda una sequenza di operazioni, guarda i casi d’uso. Se riguarda salvataggio, file, localStorage, API o database, guarda l’infrastruttura. Se riguarda input, pulsanti, form e messaggi, guarda la Presentation.
Il tempo è sufficiente per alternare spiegazione, modifica guidata e revisione. Alla fine della lezione lo studente non deve ricordare una definizione a memoria: deve saper indicare un punto del progetto, spiegare perché quel codice sta lì e mostrare una verifica del comportamento.
Passo per passo nel progetto
- Rileggi il titolo della lezione e riscrivilo come domanda pratica. Per esempio: “dove deve stare questa regola?”, “chi deve salvare questo dato?”, “come verifico questo errore?”.
- Apri solo i file necessari. Se devi toccare più di tre file, fermati e scrivi prima una mini-mappa.
- Definisci input e output del caso d’uso.
- Leggi i dati attraverso un archivio o una dipendenza esplicita.
- Chiama le regole del dominio.
- Salva il risultato senza sapere come funziona la persistenza concreta.
- Aggiorna una nota breve nel README o negli appunti se hai cambiato un confine del progetto.
Esempio da leggere lentamente
Questo esempio serve a ragionare, non a incollare codice senza capire:
function registraPrestito(comando, archivioStrumenti, archivioPrestiti) {
const strumento = archivioStrumenti.trovaPerId(comando.strumentoId);
verificaStrumentoPrestabile(strumento);
const prestito = apriPrestito(strumento.id, comando.nomePersona);
archivioPrestiti.salva(prestito);
archivioStrumenti.salva({ ...strumento, stato: "in_prestito" });
return prestito;
}
Leggilo chiedendoti che cosa cambierebbe se domani sostituissimo la pagina web con una CLI, oppure localStorage con un’API. Se l’esempio continua ad avere senso senza riscrivere tutto, il confine è probabilmente buono. Se invece un dettaglio tecnico si propaga in molti file, il progetto sta tornando fragile.
Prova di comprensione
Prima di chiudere la lezione, rispondi per iscritto a queste domande:
- Qual è la responsabilità principale trattata in questa lezione?
- In quale livello del progetto dovrebbe stare?
- Quale errore faresti se cercassi la soluzione più veloce?
- Quale piccolo test, controllo manuale o esempio dimostra che hai capito?
- Che cosa deve restare facile da cambiare dopo questa lezione?
Una risposta accettabile non deve essere lunga. Deve però usare parole concrete del Registro: strumento, prestito, stato, archivio, caso d’uso, messaggio, test.
Controllo finale
La lezione e completata quando puoi rispondere si:
- so spiegare inversione del controllo senza framework;
- il caso d’uso non sceglie l’archivio concreto;
- so dove viene scelta l’implementazione;
- so perche questo aiuta i test;
- so evitare astrazioni inutili.
Nella prossima lezione organizziamo la composizione nel punto di avvio.