Design patterns: Prototype

Mauto 3 min

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

  1. 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.
  2. 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 clone esegue una copia superficiale, quindi le proprietà che contengono oggetti vanno clonate esplicitamente in __clone() (deep copy), altrimenti clone e prototipo le condividono.
  3. 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.
  4. 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.
  5. 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.
  6. 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.
  7. 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

TORNA ALLA GUIDA

Copiato