modulo 14 di 24 / Ore 21-40
Moduli 9-16 / Checkpoint 40 ore
Rendere esplicite le dipendenze nascoste
Trovare le dipendenze nascoste nel Registro del laboratorio e renderle visibili nella firma delle funzioni, migliorando testabilità e leggibilità senza cambiare il comportamento.
Rendere esplicite le dipendenze nascoste
Questa lezione appartiene al Modulo 14 — Refactoring: migliorare senza ricominciare.
Cos’è una dipendenza nascosta
Una dipendenza nascosta è qualcosa che una funzione usa senza dichiararlo nella propria firma. Chi legge la firma non sa che quella dipendenza esiste — deve leggere il corpo per scoprirlo.
Le dipendenze nascoste più comuni nel Registro:
- variabili globali (
let archivioGlobale = []) - accesso diretto a
localStoragedentro funzioni di dominio o application - accesso diretto al DOM (
document.getElementById) dentro funzioni di logica importdi moduli concreti dentro funzioni che dovrebbero dipendere solo da contratti
Perché sono un problema
Una dipendenza nascosta rende impossibile:
- Testare la funzione senza il side effect (es. impossibile testare senza localStorage)
- Sostituire la dipendenza (es. impossibile cambiare archivio senza modificare la funzione)
- Capire cosa la funzione fa solo leggendo la firma
Tre esempi nel Registro — prima e dopo
Esempio 1: variabile globale
// ❌ Prima: dipendenza nascosta da variabile globale
let strumenti = [];
function registraStrumento(strumento) {
strumenti.push(strumento); // ← usa strumenti globale — non visibile dalla firma
}
// ✅ Dopo: dipendenza esplicita nel parametro
function registraStrumento(archivioStrumenti, strumento) {
archivioStrumenti.salva(strumento); // ← dipendenza visibile dalla firma
}
Esempio 2: localStorage diretto in una funzione di application
// ❌ Prima: dipendenza nascosta da localStorage
function trovaPerId(id) {
const lista = JSON.parse(localStorage.getItem("registro:strumenti") || "[]");
return lista.find(s => s.id === id) ?? null;
// ← usa localStorage — non visibile dalla firma
}
// ✅ Dopo: dipendenza sull'archivio, localStorage nascosto nell'adapter
function creaRegistraPrestito({ archivioStrumenti, archivioPrestiti }) {
return async function registraPrestito(comando) {
const strumento = await archivioStrumenti.trovaPerId(comando.strumentoId);
// ← usa archivioStrumenti — dipendenza visibile nel costruttore
};
}
Esempio 3: DOM diretto in una funzione di logica
// ❌ Prima: dipendenza nascosta dal DOM
function mostraStatoStrumento(id) {
const strumento = archivio.trovaPerId(id);
document.getElementById("stato-" + id).textContent = strumento.stato;
// ← usa document — non visibile dalla firma, impossibile testare
}
// ✅ Dopo: la funzione restituisce il dato, la UI lo usa
function getStatoStrumento(archivioStrumenti, id) {
const strumento = archivioStrumenti.trovaPerId(id);
return strumento ? strumento.stato : null;
// ← nessun DOM — testabile con oggetti in memoria
}
// La Presentation usa il risultato
const stato = getStatoStrumento(archivioStrumenti, id);
document.getElementById("stato-" + id).textContent = stato ?? "non trovato";
Come trovare le dipendenze nascoste nel tuo progetto
Cerca questi pattern nel codice:
localStorage.getItem(...) → dentro domain/ o application/ = dipendenza nascosta
localStorage.setItem(...) → dentro domain/ o application/ = dipendenza nascosta
document.getElementById(...) → dentro domain/ o application/ = dipendenza nascosta
let variabile = [] (globale) → usata dentro una funzione = dipendenza nascosta
import { ... } from "../infra" → dentro domain/ = dipendenza nascosta
Strumento pratico: apri ogni file in domain/ e application/. Cerca localStorage, document, fetch. Se li trovi → dipendenza nascosta da estrarre.
Come rendere una dipendenza esplicita — tre tecniche
Tecnica 1: aggiungi il parametro
La più semplice. La dipendenza diventa un parametro della funzione.
// Prima
function salvaStrumento(strumento) {
const lista = JSON.parse(localStorage.getItem("key") || "[]");
lista.push(strumento);
localStorage.setItem("key", JSON.stringify(lista));
}
// Dopo — localStorage spostato nell'adapter, archivio come parametro
function salvaStrumento(archivioStrumenti, strumento) {
archivioStrumenti.salva(strumento);
}
Tecnica 2: usa una factory (costruttore di funzioni)
Quando più funzioni condividono la stessa dipendenza, si raggruppano con una factory.
// Prima: ogni funzione riceve archivioStrumenti come parametro → ripetitivo
function registraStrumento(archivioStrumenti, strumento) { ... }
function cercaStrumenti(archivioStrumenti, filtri) { ... }
function trovaPerId(archivioStrumenti, id) { ... }
// Dopo: factory che riceve la dipendenza una volta sola
function creaGestoreStrumenti({ archivioStrumenti }) {
return {
registra: (strumento) => archivioStrumenti.salva(strumento),
cerca: (filtri) => archivioStrumenti.leggiTutti().filter(...),
trovaPerId: (id) => archivioStrumenti.trovaPerId(id)
};
}
Tecnica 3: sposta la dipendenza nel punto di composizione
Se la dipendenza si usa solo in un posto, la si porta a main.js dove tutto viene composto.
// main.js — unico posto dove si costruiscono le dipendenze concrete
const archivioStrumenti = creaArchivioStrumentiLocalStorage();
const archivioPrestiti = creaArchivioPrestitiLocalStorage();
const registraPrestito = creaRegistraPrestito({ archivioStrumenti, archivioPrestiti });
La procedura del refactoring
- Identifica la dipendenza nascosta (es.
localStorageinapplication/). - Scrivi una verifica prima di modificare (test o scenario manuale).
- Aggiungi la dipendenza come parametro o factory.
- Aggiorna il punto di chiamata per passare la dipendenza.
- Verifica che il comportamento sia identico.
- Controlla che il file ora testabile possa girare con un archivio in memoria.
Attività in classe
## Dipendenza nascosta resa esplicita
Dipendenza trovata:
- File e riga:
- Tipo: localStorage / variabile globale / document / altro
Tecnica usata: parametro / factory / punto di composizione
Firma prima:
Firma dopo:
Verifica eseguita prima:
Verifica eseguita dopo (comportamento invariato):
La funzione ora è testabile senza browser (sì/no):
Spiegazione guidata per studiare
Una dipendenza nascosta è una bugia nella firma della funzione. La firma dice “ho bisogno di questi parametri” ma il corpo dice “ho bisogno anche di localStorage e di una variabile globale”. Chi chiama la funzione non lo sa — e non può sostituire quelle dipendenze.
Il punto centrale è: rendere esplicita una dipendenza non cambia cosa fa la funzione — cambia chi decide cosa usare. Con la dipendenza nascosta, è la funzione a scegliere l’archivio. Con la dipendenza esplicita, è chi costruisce il sistema a sceglierlo — e può scegliere un archivio in memoria per i test.
Il rischio tipico è pensare che aggiungere un parametro complichi il codice. In realtà lo semplifica: chi legge la firma sa esattamente di cosa la funzione ha bisogno, senza leggere il corpo.
Passo per passo nel progetto
- Apri i file in
domain/— cercali perlocalStorage. Trovato qualcosa? È una dipendenza nascosta da estrarre. - Apri i file in
application/— stessa cosa. - Cerca variabili dichiarate fuori dalle funzioni che le usano.
- Scegli la dipendenza nascosta più critica (quella che rende impossibile il test).
- Rendila esplicita con la tecnica più appropriata.
- Verifica che il comportamento sia invariato.
Esempio da leggere lentamente
// Prima: dipendenza nascosta — impossibile testare senza localStorage
function registraStrumento(strumento) {
const lista = JSON.parse(localStorage.getItem("registro:strumenti") || "[]");
lista.push(strumento);
localStorage.setItem("registro:strumenti", JSON.stringify(lista));
}
// Non testabile: ogni test modifica il localStorage reale
// Dopo: dipendenza esplicita — testabile con archivio in memoria
function creaRegistraStrumento({ archivioStrumenti }) {
return function registraStrumento(strumento) {
archivioStrumenti.salva(strumento);
};
}
// Test con archivio in memoria — nessun localStorage coinvolto
const archivioFinto = creaArchivioStrumentiInMemoria();
const registraStrumento = creaRegistraStrumento({ archivioStrumenti: archivioFinto });
registraStrumento({ id: "s1", nome: "Trapano", categoria: "Elettrico", stato: "disponibile" });
console.assert(archivioFinto.trovaPerId("s1") !== null, "Strumento dovrebbe esistere");
Prova di comprensione
- Trova una dipendenza nascosta nel tuo progetto. Dove stava?
- Dopo averla resa esplicita, puoi testare la funzione senza browser?
- Quale tecnica hai usato (parametro, factory, punto di composizione)?
Errori da evitare
- rendere esplicita la dipendenza ma lasciare anche la versione nascosta “per sicurezza”;
- aggiungere parametri senza aggiornare tutti i chiamanti;
- confondere “rendere esplicita” con “aggiungere complessità” — è il contrario;
- fare refactoring e cambiare comportamento nello stesso passo.
Prodotto da consegnare
Può essere:
- una funzione con dipendenza nascosta trasformata in dipendenza esplicita;
- un test che verifica la funzione refactorizzata usando un archivio in memoria;
- la scheda compilata con prima, dopo e verifica.
Checklist di chiusura
- Ho trovato almeno una dipendenza nascosta nel progetto
- La dipendenza è ora visibile nella firma della funzione
- Ho aggiornato tutti i punti che chiamano la funzione modificata
- Il comportamento del Registro è identico prima e dopo
- La funzione refactorizzata può essere testata senza browser
Domande per la revisione
- Quante dipendenze nascoste hai trovato in totale? Ne hai resa esplicita solo una?
- La funzione è ora testabile senza localStorage? Come lo verifichi?
- Quale dei tre tipi di dipendenza nascosta era più comune nel tuo progetto?
Risultato atteso
Alla fine lo studente deve saper dire:
Ho trovato questa dipendenza nascosta: [localStorage / variabile globale / document]
in [file:funzione].
L'ho resa esplicita aggiungendo [parametro / factory].
Il comportamento è invariato — ho verificato con [test / scenario manuale].
La funzione ora può essere testata con un archivio in memoria.
Se questa spiegazione non è possibile, la lezione non è ancora davvero conclusa.