Design patterns: Prototype
Il pattern Prototype è un pattern creazionale che permette di creare nuovi oggetti clonando un’istanza esistente. Questo è utile quando la creazione di un nuovo oggetto richiede risorse significative e possiamo ottenere nuove istanze clonando quelle esistenti.
Caratteristiche del pattern prototype
- Creazione di Oggetti tramite clonazione: Il Prototype Pattern consente di creare nuovi oggetti copiando (clonando) un oggetto esistente, anziché istanziarlo tramite un costruttore. Questo è particolarmente utile per oggetti complessi.
- Indipendenza dei cloni: Gli oggetti prototipo possono essere utilizzati come basi per la creazione di nuovi oggetti. I nuovi oggetti creati tramite clonazione possono essere modificati senza alterare l’oggetto prototipo originale. Attenzione: in PHP
cloneesegue una copia superficiale, quindi le proprietà che contengono oggetti vanno clonate esplicitamente in__clone()(deep copy), altrimenti clone e prototipo le condividono. - Indipendenza dalla Classe concreta: Il pattern non richiede che il codice client conosca la classe concreta degli oggetti che sta creando. Può interagire con gli oggetti attraverso un’interfaccia comune.
- Semplificazione della Creazione di Oggetti complessi: Quando un oggetto è difficile o costoso da creare, il Prototype Pattern permette di definire un oggetto “modello” che può essere clonato e personalizzato.
- Efficienza: Clonare un oggetto può essere più efficiente rispetto alla creazione di un nuovo oggetto, specialmente se il processo di costruzione è complesso e richiede molte risorse.
- Supporto per la Variazione Dinamica: È facile cambiare la configurazione di un oggetto copiato senza influenzare il prototipo originale, consentendo flessibilità nella gestione degli oggetti durante l’esecuzione.
- Minor dipendenza da Costruttori: Riduce la necessità di utilizzare diversi costruttori per vari oggetti, semplificando il codice e migliorando la manutenibilità.
Esempio in PHP:
Immaginiamo di avere un sistema in cui possiamo creare diversi tipi di animali clonandoli a partire da prototipi conservati in un registro.
// Interfaccia Prototype
interface AnimalPrototype {
public function __clone();
public function getType();
public function getName();
public function setName($name);
}
// Classe Concreta per Pecora
class Sheep implements AnimalPrototype {
private $type = "Pecora";
private $name;
public function __construct($name) {
$this->name = $name;
}
public function getType() {
return $this->type;
}
public function getName() {
return $this->name;
}
public function setName($name) {
$this->name = $name;
}
public function __clone() {
// Le proprietà scalari vengono copiate automaticamente.
// Eventuali proprietà oggetto andrebbero clonate qui (deep copy).
}
}
// Classe Concreta per Mucca
class Cow implements AnimalPrototype {
private $type = "Mucca";
private $name;
public function __construct($name) {
$this->name = $name;
}
public function getType() {
return $this->type;
}
public function getName() {
return $this->name;
}
public function setName($name) {
$this->name = $name;
}
public function __clone() {
// Le proprietà scalari vengono copiate automaticamente.
// Eventuali proprietà oggetto andrebbero clonate qui (deep copy).
}
}
// Registro che conserva i prototipi e restituisce cloni
class AnimalRegistry {
private $prototypes = [];
public function addPrototype($key, AnimalPrototype $prototype) {
$this->prototypes[$key] = $prototype;
}
public function create($key): AnimalPrototype {
if (!isset($this->prototypes[$key])) {
throw new InvalidArgumentException("Prototipo non trovato: $key");
}
return clone $this->prototypes[$key];
}
}
// Configurazione: i prototipi vengono creati una sola volta
$registry = new AnimalRegistry();
$registry->addPrototype("pecora", new Sheep("Dolly"));
$registry->addPrototype("mucca", new Cow("Carolina"));
// Utilizzo: il client chiede un clone al registro, senza usare "new"
$firstSheepClone = $registry->create("pecora");
$secondSheepClone = $registry->create("pecora");
$firstCowClone = $registry->create("mucca");
// Modifichiamo un clone: il prototipo non cambia
$secondSheepClone->setName("Shaun");
echo "Clonata: " . $firstSheepClone->getType() . " " . $firstSheepClone->getName() . "\n"; // Clonata: Pecora Dolly
echo "Clonata: " . $secondSheepClone->getType() . " " . $secondSheepClone->getName() . "\n"; // Clonata: Pecora Shaun
echo "Clonata: " . $firstCowClone->getType() . " " . $firstCowClone->getName() . "\n"; // Clonata: Mucca Carolina
echo "Prototipo invariato, nuovo clone: " . $registry->create("pecora")->getName() . "\n"; // Prototipo invariato, nuovo clone: Dolly