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.

25 min Architettura del software

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:

  1. Dimenticanza: si aggiorna un posto ma non un altro — il comportamento diverge
  2. Incoerenza: !== "disponibile" in un posto, === "guasto" in un altro — stessa logica, forme diverse
  3. 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

  1. Esegui grep -rn "\.stato" src/ — trova tutte le occorrenze.
  2. Per ogni occorrenza: è un controllo che potrebbe essere centralizzato?
  3. Crea le funzioni predicato in domain/strumento.js.
  4. Sostituisci le occorrenze una alla volta.
  5. 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

  1. Esegui grep per \.stato in tutto il progetto.
  2. Conta le occorrenze — quante ci sono fuori da domain/?
  3. Crea le funzioni predicato per i controlli più frequenti.
  4. Sostituisci tutte le occorrenze fuori da domain/ con le funzioni predicato.
  5. 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.stato hai trovato fuori da domain/?
  • 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.js invece di domain/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 .stato hai eliminato fuori da domain/?
  • 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.