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à.

25 min Architettura del software

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:

MotivoArea
cambia la regola dello stato dopo un prestitodominio
cambia la struttura del prestitoapplication / domain
cambia il salvataggioinfrastructure
cambia il messaggioUI
cambia il render della listaUI

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.js separeresti 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.