modulo 14 di 24 / Ore 21-40
Moduli 9-16 / Checkpoint 40 ore
Ridurre duplicazione nei controlli sugli stati
Trovare i controlli sullo stato dello strumento ripetuti in più punti del Registro e centralizzarli in funzioni del dominio, eliminando la duplicazione.
Ridurre duplicazione nei controlli sugli stati
Questa lezione appartiene al Modulo 14 — Refactoring: migliorare senza ricominciare.
Il problema: stesso controllo in più posti
Nel Registro del laboratorio, il controllo sullo stato di uno strumento appare in molti punti:
// In registraPrestito.js
if (strumento.stato !== "disponibile") throw new Error("STRUMENTO_NON_DISPONIBILE");
// In ui.js (event listener)
if (strumento.stato === "guasto") { mostraErrore("Guasto"); return; }
// In cercaStrumenti.js
const disponibili = strumenti.filter(s => s.stato === "disponibile");
// In renderListaStrumenti.js
const badge = strumento.stato === "in_prestito" ? "occupato" : "libero";
Ogni punto controlla lo stato in modo leggermente diverso. Se si aggiunge un quinto stato ("dismesso") o si cambia il nome di uno stato esistente, bisogna trovare e aggiornare tutti questi punti.
Perché è un problema
La duplicazione dei controlli sugli stati causa tre problemi:
- Dimenticanza: si aggiorna un posto ma non un altro — il comportamento diverge
- Incoerenza:
!== "disponibile"in un posto,=== "guasto"in un altro — stessa logica, forme diverse - Manutenzione: aggiungere uno stato richiede di cercare in tutta la codebase
Come centralizzare i controlli nel dominio
La soluzione è creare funzioni predicato nel dominio — funzioni che rispondono sì/no a una domanda sullo stato:
// domain/strumento.js — unico posto dove si definiscono i controlli sugli stati
function isDisponibile(strumento) {
return strumento.stato === "disponibile";
}
function isInPrestito(strumento) {
return strumento.stato === "in_prestito";
}
function isGuasto(strumento) {
return strumento.stato === "guasto";
}
function isInManutenzione(strumento) {
return strumento.stato === "in_manutenzione";
}
function puoEsserePrestato(strumento) {
return isDisponibile(strumento);
}
function puoEssereSegnalatoGuasto(strumento) {
return isDisponibile(strumento) || isInManutenzione(strumento);
}
Ora ogni parte del sistema usa queste funzioni invece di controllare strumento.stato direttamente.
Come si usa in pratica
// application/registraPrestito.js — usa la funzione del dominio
import { puoEsserePrestato } from "../domain/strumento.js";
function registraPrestito(comando) {
const strumento = archivioStrumenti.trovaPerId(comando.strumentoId);
if (!strumento) throw new Error("STRUMENTO_NON_TROVATO");
if (!puoEsserePrestato(strumento)) throw new Error("STRUMENTO_NON_DISPONIBILE");
// ...
}
// application/cercaStrumenti.js — usa la funzione del dominio
import { isDisponibile } from "../domain/strumento.js";
function filtraDisponibili(strumenti) {
return strumenti.filter(isDisponibile);
}
// presentation/renderListaStrumenti.js — usa la funzione del dominio per le classi CSS
import { isDisponibile, isInPrestito } from "../domain/strumento.js";
function renderStrumento(strumento) {
const classeStato = isInPrestito(strumento) ? "occupato" : "disponibile";
return `<li class="strumento ${classeStato}">${strumento.nome}</li>`;
}
Il vantaggio concreto: aggiungere uno stato
Se si aggiunge lo stato "dismesso" per strumenti fuori uso permanentemente:
// Senza centralizzazione: cerca in tutta la codebase
// grep "strumento.stato" → 15 occorrenze da aggiornare
// Con centralizzazione: aggiorna solo domain/strumento.js
function isDisponibile(strumento) {
return strumento.stato === "disponibile";
// "dismesso" non è disponibile → già escluso correttamente
}
function puoEsserePrestato(strumento) {
return isDisponibile(strumento);
// "dismesso" non può essere prestato → già corretto
}
// Aggiungi solo le nuove funzioni necessarie:
function isDismesso(strumento) {
return strumento.stato === "dismesso";
}
Un solo file da aggiornare. Tutto il resto funziona correttamente senza modifiche.
Come trovare le duplicazioni nel tuo progetto
# Cerca tutte le occorrenze di strumento.stato nel progetto
grep -rn "strumento\.stato" src/
Per ogni occorrenza trovata: questa condizione dovrebbe stare nel dominio come funzione predicato?
Procedura guidata
- Esegui
grep -rn "\.stato" src/— trova tutte le occorrenze. - Per ogni occorrenza: è un controllo che potrebbe essere centralizzato?
- Crea le funzioni predicato in
domain/strumento.js. - Sostituisci le occorrenze una alla volta.
- Verifica dopo ogni sostituzione che il comportamento sia invariato.
Attività in classe
## Duplicazioni trovate
Occorrenze di strumento.stato trovate:
- [file:riga]: [controllo]
- [file:riga]: [controllo]
Funzioni predicato create in domain/strumento.js:
- isDisponibile
- isInPrestito
- (altre)
Sostituzioni applicate:
- [file:riga]: [prima] → [dopo]
Verifica del comportamento: identico (sì/no)
Punto rimasto senza centralizzazione (e perché):
Spiegazione guidata per studiare
La duplicazione dei controlli sugli stati è uno dei refactoring più utili e meno rischiosi da fare nel Registro. È utile perché elimina un’intera categoria di bug (dimenticare di aggiornare un posto). È poco rischioso perché si sostituisce codice identico con chiamate a funzioni con lo stesso risultato.
Il punto centrale è: ogni confronto diretto strumento.stato === "..." nel codice che non è domain/strumento.js è una potenziale duplicazione. Il dominio dovrebbe essere l’unico posto che “sa” come si chiama uno stato.
Il rischio tipico è centralizzare solo alcuni controlli e lasciarne altri dispersi. La grep sistematica prima di iniziare previene questo problema.
Passo per passo nel progetto
- Esegui grep per
\.statoin tutto il progetto. - Conta le occorrenze — quante ci sono fuori da
domain/? - Crea le funzioni predicato per i controlli più frequenti.
- Sostituisci tutte le occorrenze fuori da
domain/con le funzioni predicato. - Verifica con uno scenario manuale: presta, chiudi, segnala guasto — tutto funziona?
Esempio da leggere lentamente
// Ricerca duplicazioni: prima
// In registraPrestito.js: strumento.stato !== "disponibile"
// In ui.js: strumento.stato === "guasto"
// In cercaStrumenti.js: strumento.stato === "disponibile"
// In renderListaStrumenti.js: strumento.stato === "in_prestito"
// → 4 occorrenze fuori dal dominio
// Dopo centralizzazione:
// domain/strumento.js:
// isDisponibile, isGuasto, isInPrestito → unico posto
// Tutte le altre occorrenze usano queste funzioni:
// if (!puoEsserePrestato(strumento)) → in registraPrestito.js
// if (isGuasto(strumento)) → in ui.js
// strumenti.filter(isDisponibile) → in cercaStrumenti.js
// isInPrestito(strumento) ? "occupato" : "ok" → in renderListaStrumenti.js
// → 0 occorrenze di .stato fuori dal dominio
Prova di comprensione
- Quante occorrenze di
strumento.statohai trovato fuori dadomain/? - Dopo il refactoring, aggiungere lo stato
"dismesso"richiederebbe di modificare quanti file? - La funzione
puoEsserePrestatoè nel posto giusto? Perché?
Errori da evitare
- centralizzare solo i controlli “più importanti” e lasciare gli altri — la duplicazione persiste;
- mettere le funzioni predicato in un file
utils.jsinvece didomain/strumento.js— appartengono al dominio; - fare tutte le sostituzioni in una volta senza verifiche intermedie.
Prodotto da consegnare
Può essere:
- le funzioni predicato aggiunte in
domain/strumento.js; - il diff delle sostituzioni applicate in altri file;
- la verifica che aggiungere uno stato ora richiede di modificare solo
domain/strumento.js.
Checklist di chiusura
- Ho eseguito grep per trovare tutte le occorrenze di
.stato - Ho creato le funzioni predicato in
domain/strumento.js - Ho sostituito tutte le occorrenze fuori da
domain/con le funzioni predicato - Il comportamento del Registro è identico prima e dopo
- Aggiungere un nuovo stato richiede ora di modificare solo
domain/strumento.js
Domande per la revisione
- Quante occorrenze di
.statohai eliminato fuori dadomain/? - Quante funzioni predicato hai creato? Erano tutte necessarie?
- Il refactoring ha reso il codice più leggibile o più complesso?
Risultato atteso
Alla fine lo studente deve saper dire:
Ho trovato [N] occorrenze di strumento.stato fuori da domain/.
Ho creato queste funzioni predicato: [lista].
Ho sostituito le occorrenze — il comportamento è invariato.
Ora aggiungere uno stato richiede solo di modificare domain/strumento.js.
Se questa spiegazione non è possibile, la lezione non è ancora davvero conclusa.