modulo 04 di 24 / Ore 0-20
Moduli 1-8 / Checkpoint 20 ore
Coesione spiegata con funzioni del Registro
Capire la coesione osservando funzioni e file del Registro del laboratorio: quando le parti lavorano per lo stesso scopo e quando invece sono tenute insieme per comodità.
Coesione spiegata con funzioni del Registro
La coesione misura quanto le parti di una funzione, di un file o di un modulo lavorano per lo stesso scopo.
Un codice coeso è più facile da capire perché tutto quello che trovi in un punto ha senso insieme.
Un codice poco coeso sembra una stanza usata come deposito: contiene cose utili, ma non sai perché siano finite nello stesso posto.
Obiettivo della lezione
Alla fine devi saper:
- spiegare la coesione con esempi del Registro;
- riconoscere una funzione poco coesa;
- distinguere coesione di funzione e coesione di file;
- mappare i gruppi presenti in
main.js; - proporre una separazione futura senza riscrivere tutto.
La domanda guida è:
queste righe lavorano davvero per lo stesso scopo?
Coesione in una funzione
Guarda questa funzione:
function mostraMessaggio(testo) {
messaggioElement.textContent = testo;
}
È molto semplice.
Ha alta coesione: tutto quello che fa riguarda mostrare un messaggio.
Ora guarda questa:
function aggiornaRegistroDopoPrestito(strumento, prestito) {
strumento.stato = "in_prestito";
prestiti.push(prestito);
localStorage.setItem("strumenti", JSON.stringify(strumenti));
localStorage.setItem("prestiti", JSON.stringify(prestiti));
messaggioElement.textContent = "Prestito registrato";
mostraStrumenti(strumenti);
}
Qui ci sono operazioni diverse:
- modifica lo stato di uno strumento;
- aggiunge un prestito;
- salva strumenti;
- salva prestiti;
- mostra un messaggio;
- ridisegna la lista.
La funzione ha bassa coesione perché tiene insieme cose che appartengono a scopi diversi.
Non è solo “lunga”. È mista.
Coesione e motivo per cambiare
La lezione precedente parlava di motivi per cambiare.
La coesione è collegata:
- una funzione coesa cambia per pochi motivi collegati;
- una funzione poco coesa cambia per motivi lontani tra loro.
Nel nostro esempio, aggiornaRegistroDopoPrestito cambia se:
| Motivo | Area |
|---|---|
| cambia la regola dello stato dopo un prestito | dominio |
| cambia la struttura del prestito | application / domain |
| cambia il salvataggio | infrastructure |
| cambia il messaggio | UI |
| cambia il render della lista | UI |
Questa tabella mostra bassa coesione.
Coesione in un file
Nel Registro fragile, main.js può contenere quasi tutto:
main.js
dati iniziali
riferimenti DOM
render lista
ricerca
regole prestito
cambio stato
localStorage
messaggi
Questo file ha bassa coesione.
Non perché sia lungo in assoluto.
Ha bassa coesione perché contiene responsabilità lontane tra loro.
Un file più coeso ha un tema riconoscibile:
statoStrumento.js
stati ammessi
controlli sugli stati
regole legate allo stato
Oppure:
renderStrumenti.js
crea HTML per uno strumento
aggiorna la lista visibile
mostra stato vuoto
Oppure:
archivioLocale.js
legge dati salvati
scrive dati salvati
gestisce dati mancanti o corrotti
Quando apri un file, dovresti capire che tipo di cose ci troverai.
Esempio di alta coesione nella UI
Queste funzioni stanno bene vicine:
function mostraStrumenti(strumenti) {
strumentiElement.innerHTML = strumenti.map(renderStrumento).join("");
}
function renderStrumento(strumento) {
return `<li>${strumento.nome} - ${strumento.stato}</li>`;
}
function mostraListaVuota() {
strumentiElement.innerHTML = "<p>Nessuno strumento trovato.</p>";
}
Lavorano tutte sulla presentazione della lista.
Non decidono regole di prestito.
Non salvano dati.
Non leggono da localStorage.
Questa è coesione: funzioni diverse, ma stesso scopo.
Esempio di bassa coesione nella UI
Questa funzione invece mescola troppo:
function mostraStrumenti(strumenti) {
const strumentiPrestabili = strumenti.filter((strumento) => {
return strumento.stato !== "guasto" && strumento.stato !== "in_manutenzione";
});
localStorage.setItem("ultimaLista", JSON.stringify(strumentiPrestabili));
strumentiElement.innerHTML = strumentiPrestabili.map(renderStrumento).join("");
}
Il nome dice “mostra strumenti”.
Ma la funzione:
- applica una regola di prestabilità;
- salva dati in
localStorage; - renderizza HTML.
Sono tre scopi diversi.
Il problema non è usare filter.
Il problema è che una funzione di render sta decidendo regole e salvando dati.
Coesione concettuale del dominio
Nel Registro alcuni gruppi di idee stanno bene insieme:
Strumento
id
nome
categoria
stato
regole sugli stati
Prestito
id
strumentoId
persona
data apertura
data chiusura
regole di chiusura
Ricerca
testo
categoria
stato
criteri di filtro
UI
input
bottoni
messaggi
render
Questi gruppi non sono ancora obbligatoriamente file.
Sono gruppi di significato.
La coesione nasce prima nel ragionamento e poi nella struttura del progetto.
Segnali di bassa coesione
Un blocco ha probabilmente bassa coesione quando:
- il nome è generico, per esempio
gestisciTutto; - contiene sia UI sia regole;
- contiene sia regole sia
localStorage; - modifica dati e costruisce HTML;
- usa molte variabili globali diverse;
- contiene commenti che separano sezioni molto diverse;
- cresce ogni volta che aggiungi una funzionalità;
- per spiegarlo devi usare troppe volte “e poi”.
Esempio:
Questa funzione legge input e poi controlla lo stato e poi salva e poi mostra
un messaggio e poi aggiorna la lista.
La ripetizione di “e poi” è spesso un indizio.
Coesione non significa isolamento totale
Una funzione coesa può chiamare altre funzioni.
Il problema non è collaborare.
Il problema è mescolare scopi.
Esempio accettabile:
function mostraRisultatiRicerca(strumenti) {
if (strumenti.length === 0) {
mostraListaVuota();
return;
}
mostraStrumenti(strumenti);
}
Questa funzione resta nella UI.
Coordina una piccola decisione di presentazione: lista vuota o lista piena.
Non sta decidendo se uno strumento può essere prestato.
Attività guidata
Prendi il tuo main.js fragile e crea una mappa:
## Gruppi di codice in main.js
### Dati iniziali
### Riferimenti UI
### Render e messaggi
### Ricerca
### Regole sugli strumenti
### Prestiti
### Salvataggio
Sotto ogni sezione inserisci nomi di funzioni, righe o blocchi.
Poi rispondi:
Quali gruppi sono molto pieni?
Quali gruppi non dovrebbero stare nello stesso file?
Quale gruppo sarebbe più facile separare per primo?
Quale gruppo è più rischioso separare senza test?
Micro-sfida
Trova una funzione con bassa coesione e proponi due o tre funzioni più coese.
Formato:
Funzione attuale:
registraPrestitoFragile
Responsabilità mescolate:
input, regole, salvataggio, messaggi
Possibili funzioni:
leggiInputPrestito
puoEsserePrestato
salvaPrestito
mostraEsitoPrestito
Prima separazione consigliata:
puoEsserePrestato, perché contiene una regola testabile.
Non devi implementarle tutte.
Devi capire quali scopi diversi contiene il blocco.
Prodotto da ottenere
Alla fine della lezione devi avere:
- mappa dei gruppi presenti in
main.js; - una funzione poco coesa individuata;
- elenco delle responsabilità mescolate;
- proposta di due o tre funzioni più coese;
- prima separazione consigliata e motivata.
Domande di comprensione
Rispondi per iscritto:
- Che cosa significa coesione?
- Perché una funzione che fa molte cose diverse è più difficile da cambiare?
- Quando più funzioni possono stare nello stesso file?
- Perché una funzione di render non dovrebbe salvare dati?
- Quale gruppo del tuo
main.jssepareresti per primo?
Controllo finale
La lezione è completata quando puoi dire sì a queste domande:
- so spiegare la coesione con parole mie;
- so riconoscere una funzione poco coesa;
- ho mappato i gruppi di codice in
main.js; - so distinguere coesione di funzione e coesione di file;
- ho individuato almeno un gruppo da separare in futuro;
- ho capito che coesione non significa creare funzioni microscopiche senza motivo.
Nel prossimo passo parleremo di accoppiamento: quanto una parte del codice dipende dai dettagli di un’altra.