modulo 14 di 24 / Ore 21-40

Moduli 9-16 / Checkpoint 40 ore

Estrarre funzioni piccole da funzioni lunghe

Come identificare funzioni lunghe nel Registro e estrarle in funzioni più piccole con nomi precisi — con esempio concreto before/after su un event listener o caso d'uso.

25 min Architettura del software

Estrarre funzioni piccole da funzioni lunghe

Questa lezione appartiene al Modulo 14 — Refactoring.

Il lavoro continua il progetto guida, il Registro del laboratorio scolastico. In questa lezione impari a riconoscere le funzioni troppo lunghe nel Registro e a estrarle in funzioni più piccole con nomi precisi.

Obiettivo della lezione

Identificare almeno una funzione lunga nel tuo Registro (più di 20 righe) e scomporla in funzioni più piccole con nomi che descrivono esattamente cosa fanno. Il comportamento deve restare identico.

Alla fine devi avere: la funzione originale lunga sostituita da chiamate a funzioni estratte, con nomi chiari e responsabilità singola.

Come riconoscere una funzione da estrarre

Una funzione è candidata all’estrazione quando:

  • ha più di 15-20 righe
  • fa più di una cosa (lo puoi vedere dai commenti interni)
  • ha blocchi di codice separati da righe vuote che svolgono compiti distinti
  • contiene logica che potresti voler riusare in un altro punto

Il segnale più chiaro: se devi aggiungere un commento dentro la funzione per spiegare cosa fa una parte di codice, quella parte è un buon candidato per l’estrazione.

Nel Registro del laboratorio

Le funzioni più spesso troppo lunghe nel Registro sono:

  • i gestori degli eventi nella Presentation (leggono input + validano + chiamano caso d’uso + aggiornano UI)
  • i casi d’uso che coordinano più operazioni senza separazione chiara
  • l’adapter localStorage quando gestisce mapping e validazione nella stessa funzione

Procedura guidata

  1. Apri ogni file del Registro e conta le righe di ogni funzione.
  2. Identifica le funzioni con più di 15 righe.
  3. Per ogni funzione, identifica i “blocchi” di logica (separati da righe vuote o commenti).
  4. Estrai ogni blocco in una funzione con un nome preciso.
  5. Sostituisci il blocco originale con la chiamata alla funzione estratta.
  6. Verifica che il comportamento sia invariato.
  7. Commita.

Attività in classe

Compila questa scheda prima di iniziare:

## Analisi funzioni lunghe

File: ___
Funzione più lunga: ___ (N righe)
Blocchi identificati:
  1. (descrizione)
  2. (descrizione)
  3. (descrizione)

Nomi proposti per le funzioni da estrarre:
  1. ___
  2. ___
  3. ___

Spiegazione guidata per studiare

L’estrazione di funzioni è il refactoring più semplice e più utile. Il principio è: ogni funzione fa una sola cosa, e il suo nome descrive cosa fa. Se il nome della funzione usa “e” o “oppure”, probabilmente fa più di una cosa.

Un errore comune è estrarre funzioni private che non dicono nulla: _passo1, _helper, _aux. Il nome deve essere preciso: validaInputPrestito, aggiornaStratoStrumento, costruisciMessaggioSuccesso. Se non riesci a trovare un nome preciso, probabilmente il blocco non ha ancora una responsabilità chiara.

Passo per passo nel progetto

Esempio concreto: un gestore di eventi nella Presentation troppo lungo.

Versione originale con tutto nella stessa funzione:

// presentation/azioni.js — PRIMA
btnPresta.addEventListener("click", () => {
  // parte 1: leggi e valida input
  const strumentoId = inputId.value.trim();
  if (!strumentoId) {
    messaggioUscita.textContent = "Inserisci un ID strumento valido.";
    return;
  }

  // parte 2: chiama il caso d'uso
  let prestito;
  try {
    prestito = apriPrestito({ strumentoId }, archivioStrumenti, archivioPrestiti);
  } catch (errore) {
    const messaggi = {
      "STRUMENTO_NON_TROVATO": "Strumento non trovato.",
      "STRUMENTO_NON_DISPONIBILE": "Lo strumento non è disponibile.",
      "ARCHIVIO_NON_DISPONIBILE": "Errore nel salvataggio."
    };
    messaggioUscita.textContent = messaggi[errore.message] ?? "Operazione non riuscita.";
    return;
  }

  // parte 3: aggiorna la UI
  messaggioUscita.textContent = `Prestito aperto. ID: ${prestito.id}`;
  inputId.value = "";
  aggiornaListaStrumenti(archivioStrumenti);
});

Questa funzione fa tre cose: legge/valida input, chiama il caso d’uso, aggiorna la UI. Estrazione:

// presentation/azioni.js — DOPO
function leggiInputStrumentoId() {
  return inputId.value.trim();
}

function mostraErrore(messaggioErrore) {
  const messaggi = {
    "STRUMENTO_NON_TROVATO": "Strumento non trovato.",
    "STRUMENTO_NON_DISPONIBILE": "Lo strumento non è disponibile.",
    "ARCHIVIO_NON_DISPONIBILE": "Errore nel salvataggio."
  };
  messaggioUscita.textContent = messaggi[messaggioErrore] ?? "Operazione non riuscita.";
}

function mostraSuccessoPrestito(prestito) {
  messaggioUscita.textContent = `Prestito aperto. ID: ${prestito.id}`;
  inputId.value = "";
  aggiornaListaStrumenti(archivioStrumenti);
}

btnPresta.addEventListener("click", () => {
  const strumentoId = leggiInputStrumentoId();
  if (!strumentoId) {
    mostraErrore("INPUT_VUOTO"); // può essere gestito in mostraErrore
    return;
  }
  try {
    const prestito = apriPrestito({ strumentoId }, archivioStrumenti, archivioPrestiti);
    mostraSuccessoPrestito(prestito);
  } catch (errore) {
    mostraErrore(errore.message);
  }
});

Il gestore del click ora ha 10 righe invece di 25. Ogni funzione estratta ha un nome preciso e una responsabilità sola.

Secondo esempio: estrazione dall’adapter localStorage.

// infrastructure/archivioStrumentiLocalStorage.js — PRIMA
export function caricaTutti() {
  const raw = localStorage.getItem("registro_strumenti");
  if (!raw) return [];
  try {
    const dati = JSON.parse(raw);
    if (!Array.isArray(dati)) return [];
    return dati.map(dto => ({
      id: dto.id,
      nome: dto.name,
      categoria: dto.category,
      stato: dto.status
    }));
  } catch {
    return [];
  }
}

// DOPO — funzioni estratte con nomi precisi
function leggiDaLocalStorage(chiave) {
  try {
    const raw = localStorage.getItem(chiave);
    if (!raw) return null;
    return JSON.parse(raw);
  } catch {
    return null;
  }
}

function dtoToStrumento(dto) {
  return {
    id: dto.id,
    nome: dto.name,
    categoria: dto.category,
    stato: dto.status
  };
}

export function caricaTutti() {
  const dati = leggiDaLocalStorage("registro_strumenti");
  if (!Array.isArray(dati)) return [];
  return dati.map(dtoToStrumento);
}

Prova di comprensione

Rispondi per iscritto:

  • Quale funzione nel tuo Registro era la più lunga? Quante righe aveva?
  • Quali blocchi hai identificato dentro quella funzione?
  • Come hai scelto i nomi delle funzioni estratte?
  • Dopo l’estrazione, la funzione originale è diventata più facile da leggere? Perché?

Errori da evitare

  • estrarre funzioni con nomi generici come _helper o _utils;
  • estrarre blocchi di codice che non hanno una responsabilità chiara — prima chiariscila, poi estrai;
  • estrarre funzioni che vengono usate solo una volta senza un buon motivo (aggiunge complessità senza beneficio);
  • dimenticare di verificare il comportamento dopo l’estrazione.

Prodotto da consegnare

Almeno una funzione lunga del Registro scomposta in funzioni più piccole con nomi precisi. Come verifica: mostra il before e l’after del codice e spiega verbalmente il nome e il ruolo di ogni funzione estratta.

Checklist di chiusura

  • ho identificato almeno una funzione con più di 15 righe;
  • i blocchi di codice sono stati estratti in funzioni con nomi precisi;
  • ogni funzione estratta fa una sola cosa;
  • il comportamento è identico prima e dopo l’estrazione;
  • ho committato le modifiche con un messaggio refactor: estrae funzioni da [nome funzione].

Risultato atteso

Ho estratto funzioni da: [nome funzione originale] in [file].
Prima aveva N righe. Dopo la funzione principale ha M righe.
Funzioni estratte: [lista con nomi].
Ogni funzione estratta fa una sola cosa: [descrizione per ognuna].
I test passano. Commit: "refactor: estrae funzioni da gestore click btnPresta".