Il problema: costruzione costosa o complessa
Hai un oggetto ReportTemplate con 30 proprieta: intestazione, font, margini, colori, header, footer, watermark, impostazioni di stampa. Crearlo da zero richiede un costruttore con decine di parametri o un builder con altrettante chiamate. Ma nella pratica, il 90% dei report parte dallo stesso template base con piccole variazioni: il report mensile e identico al settimanale tranne che per il titolo e il range di date.
Il Prototype Pattern propone un approccio diverso: invece di costruire un nuovo oggetto da zero, clona un oggetto esistente e modifica solo cio che cambia. Il clone e una copia indipendente — modificarlo non tocca l'originale.
Cos'e il Prototype Pattern: definizione formale
Il Gang of Four definisce il Prototype come un pattern creazionale che "specifica i tipi di oggetti da creare usando un'istanza prototipica, e crea nuovi oggetti copiando questo prototipo". In PHP, il meccanismo fondamentale e la keyword clone.
La struttura prevede: un'interfaccia Prototype con un metodo clone() (in PHP implicito tramite clone), un ConcretePrototype che implementa la clonazione, e un Client che clona il prototipo invece di istanziare con new.
clone e __clone in PHP
PHP offre supporto nativo per il Prototype Pattern tramite clone. Per default, clone esegue una shallow copy: copia le proprieta scalari ma non clona gli oggetti annidati — questi restano riferimenti condivisi. Per una deep copy serve implementare __clone():
-
Shallow clone:
$copy = clone $original— le proprieta scalari sono copiate, gli oggetti annidati sono condivisi -
Deep clone: nel metodo
__clone()cloni manualmente gli oggetti annidati:$this->header = clone $this->header - Selective clone: cloni solo gli oggetti che devono essere indipendenti, condividi quelli immutabili
Esempio teorico: template di notifiche
Un sistema di notifiche ha template predefiniti: WelcomeEmail, InvoiceEmail, ReminderEmail. Ogni template ha layout, stili, header, footer, traduzioni. Invece di ricostruire tutto ogni volta:
- Crei un
TemplateRegistryche mantiene i prototipi:$registry->register('welcome', $welcomeTemplate) - Per ogni email, cloni il prototipo:
$email = $registry->get('welcome')(internamente faclone) - Personalizzi il clone:
$email->setRecipient($user)->setName($user->name) - L'originale resta intatto per il prossimo utilizzo
Esempio teorico: configurazioni di gioco
Un gioco ha personaggi con centinaia di attributi: statistiche, inventario, abilita, aspetto. Creare un personaggio da zero e lento. Il Prototype Pattern permette di creare "archetipi" (guerriero, mago, ladro) e clonarli come punto di partenza. Il giocatore personalizza il clone senza ripartire da zero.
Lo stesso concetto si applica a qualsiasi sistema con configurazioni complesse: ambienti di deploy (staging clonato da production con URL diverso), configurazioni di test (setup base clonato con variazioni per ogni test), documenti (contratto standard clonato e personalizzato per ogni cliente).
Prototype vs Builder vs Factory
- Factory: crea oggetti nuovi da zero. Il client chiede "dammi un oggetto" e la factory lo costruisce.
- Builder: costruisce oggetti complessi passo per passo. Il client guida la costruzione.
- Prototype: duplica un oggetto esistente. Il client parte da una copia e la modifica.
Il Prototype brilla quando la costruzione e costosa (molte proprieta, calcoli, query) e la variazione e minima (il 90% delle proprieta resta uguale). Se ogni oggetto e completamente diverso, il Prototype non offre vantaggi.
Quando usare il Prototype
- Usa il Prototype quando la creazione di un oggetto e significativamente più costosa della sua clonazione
- Usa il Prototype quando il sistema lavora con variazioni minime di un oggetto base
- Usa il Prototype quando vuoi evitare una gerarchia di factory parallela alla gerarchia dei prodotti
- Non usare il Prototype se l'oggetto ha riferimenti circolari: il deep clone diventa un incubo
- Non usare il Prototype se ogni istanza e completamente diversa: clonare e poi sovrascrivere tutto e peggio che costruire da zero
Il Prototype Pattern e il pattern dell'efficienza pragmatica: non reinventare cio che esiste già. In PHP, con il supporto nativo di clone e __clone(), e uno dei pattern più semplici da implementare e più utili da avere nel toolkit.
Top comments (0)