Il Design Pattern Template Method in PHP: riusare la struttura, personalizzare i dettagli

by theArchitect
SHARE
Il Design Pattern Template Method in PHP: riusare la struttura, personalizzare i dettagli
© Guida-HTML5.it

Introduzione

Tra i design pattern più utili in PHP c’è il Template Method, perfetto quando hai più classi che condividono lo stesso flusso di lavoro, ma differiscono in alcuni passaggi specifici. In pratica, ti permette di definire una struttura comune in una classe base e lasciare alle sottoclassi la libertà di implementare i dettagli variabili.

Questo pattern è molto pratico in scenari reali: importazione di file, generazione di report, elaborazione di pagamenti, invio di notifiche o gestione di workflow. In tutti questi casi, il processo generale è uguale, ma alcune fasi cambiano in base al contesto.

Il vantaggio principale è semplice: eviti duplicazione di codice e mantieni il controllo sul flusso principale, riducendo il rischio di errori e rendendo il codice più coerente.

Codice completo

<?php

declare(strict_types=1);

/**
 * Classe astratta che definisce il "template" dell´algoritmo.
 * Il metodo pubblico process() stabilisce l´ordine delle operazioni.
 */
abstract class ReportGenerator
{
    // Template Method: definisce il flusso generale
    final public function process(): void
    {
        $this->loadData();
        $data = $this->transformData();
        $report = $this->buildReport($data);
        $this->output($report);
    }

    // Passaggi comuni o parzialmente comuni
    protected function loadData(): void
    {
        echo "Caricamento dati di base...n";
    }

    // Hook method: ogni sottoclasse può personalizzare l´elaborazione
    abstract protected function transformData(): array;

    // Passaggio comune che usa il risultato della trasformazione
    protected function buildReport(array $data): string
    {
        $lines = ["=== REPORT ==="];

        foreach ($data as $row) {
            $lines[] = "- " . $row;
        }

        return implode("n", $lines);
    }

    // Output finale, comune a tutte le sottoclassi
    protected function output(string $report): void
    {
        echo $report . "n";
    }
}

/**
 * Report per vendite.
 */
class SalesReportGenerator extends ReportGenerator
{
    protected function transformData(): array
    {
        return [
            "Ordine #1001 - 120.50 EUR",
            "Ordine #1002 - 89.90 EUR",
            "Ordine #1003 - 240.00 EUR",
        ];
    }
}

/**
 * Report per utenti registrati.
 */
class UsersReportGenerator extends ReportGenerator
{
    protected function transformData(): array
    {
        return [
            "Mario Rossi - attivo",
            "Laura Bianchi - in attesa",
            "Giulia Verdi - attivo",
        ];
    }
}

// Esecuzione
$salesReport = new SalesReportGenerator();
$salesReport->process();

echo "n";

$usersReport = new UsersReportGenerator();
$usersReport->process();

Spiegazione

Il Template Method si basa su una classe astratta che contiene il flusso principale dell’algoritmo. Nel nostro esempio, la classe ReportGenerator definisce il metodo process(), che rappresenta la sequenza standard delle operazioni:

  • caricare i dati;
  • trasformarli;
  • costruire il report;
  • mostrare il risultato.

Il punto chiave è che process() è dichiarato final. Questo significa che le sottoclassi non possono modificarne il comportamento. È una scelta importante: il pattern funziona bene proprio quando vuoi proteggere l’ordine delle operazioni, evitando che ogni classe lo cambi in modo arbitrario.

La personalizzazione avviene nel metodo astratto transformData(). Ogni sottoclasse deve implementarlo e fornire i dati specifici da elaborare. Questo è il vero “gancio” del pattern: la struttura resta uguale, ma il contenuto cambia.

Nel caso di SalesReportGenerator, il report contiene ordini e importi. In UsersReportGenerator, invece, vengono mostrati utenti e stato dell’account. Entrambe le classi seguono lo stesso processo, ma restituiscono dati diversi.

Un altro aspetto utile è il metodo buildReport(), che centralizza la formattazione del risultato. In questo modo, se domani vuoi cambiare lo stile del report, lo fai in un solo punto e tutte le sottoclassi ne beneficiano automaticamente.

Questo pattern è molto efficace quando il flusso ha passaggi invarianti e passaggi variabili. Se invece ogni classe dovesse gestire un processo completamente diverso, il Template Method non sarebbe la scelta giusta.

Best practice

  • Usa il pattern solo quando esiste davvero una struttura comune: forzarlo in casi troppo diversi rende il codice rigido e poco leggibile.
  • Proteggi il metodo template con final: così eviti che le sottoclassi alterino l’ordine dei passaggi fondamentali.
  • Lascia alle sottoclassi solo i punti di variazione: non spostare troppa logica nelle classi figlie, altrimenti perdi i vantaggi dell’astrazione.
  • Riduci la complessità della classe base: se la classe astratta diventa troppo grande, valuta di estrarre alcune responsabilità in classi di supporto.
  • Usa metodi hook con moderazione: piccoli metodi opzionali possono aumentare la flessibilità, ma troppi hook rendono il flusso difficile da seguire.
  • Documenta bene il contratto della classe base: chi estende la classe deve sapere quali metodi implementare e quali regole rispettare.

Quando conviene davvero

Il Template Method è particolarmente indicato per:

  • importazione di dati da fonti diverse ma con pipeline simile;
  • generazione di documenti con struttura comune;
  • processi di validazione e salvataggio con passaggi standard;
  • workflow applicativi in cui alcune fasi non devono essere alterate.

Quando evitarlo

Evitalo se il tuo obiettivo è solo riutilizzare qualche funzione isolata: in quel caso, spesso bastano composizione, helper class o funzioni ben progettate. Il Template Method è utile quando vuoi riutilizzare un intero algoritmo, non solo singole porzioni di codice.

Riepilogo

Il Template Method è un pattern semplice ma molto potente in PHP. Ti consente di definire una struttura comune in una classe base e di lasciare alle sottoclassi solo i dettagli variabili. È ideale per workflow ripetuti, processi ordinati e casi in cui vuoi evitare duplicazione di logica.

Nel nostro esempio, la classe astratta ha stabilito il flusso del report, mentre le sottoclassi hanno personalizzato la trasformazione dei dati. Questo approccio migliora la manutenzione, rende il codice più pulito e riduce il rischio di incoerenze tra implementazioni simili.

In sintesi:

  • la classe base definisce il processo;
  • le sottoclassi implementano i passaggi variabili;
  • il flusso principale resta stabile e controllato;
  • il codice diventa più riusabile e più facile da estendere.

Approfondisci con risorse ufficiali

  • PHP Manual - Classi astratte: documentazione ufficiale su classi astratte e metodi astratti.
  • PHP Manual - Metodi final: utile per capire come bloccare l’override del template method.
  • PHP Manual - OOP: panoramica completa sulla programmazione orientata agli oggetti in PHP.
  • Design Patterns - Template Method: riferimento classico per approfondire il pattern a livello concettuale.

SHARE