Design patterns: Prototype

data: · 3 min
autore: Mauto
serie: Design Patterns/ parte 6/24

I pattern classici della Gang of Four, uno per puntata, con esempi pratici in PHP.

  1. Design Patterns
  2. Design patterns: Singleton
  3. Design patterns: Factory Method
  4. Design patterns: Abstract Factory
  5. Design patterns: Builder
  6. Design patterns: Prototype
  7. Design patterns: Adapter
  8. Design patterns: Bridge, cos’è e come usarlo (esempio PHP)
  9. Design patterns: Composite
  10. Design patterns: Decorator
  11. Design patterns: Facade, cos’è e come usarlo (esempio PHP)
  12. Design patterns: Flyweight
  13. Design patterns: Proxy
  14. Design patterns: Chain of Responsibility, cos’è (esempio PHP)
  15. Design patterns: Command
  16. Design patterns: Interpreter
  17. Design patterns: Iterator
  18. Design patterns: Mediator
  19. Design patterns: Memento
  20. Design patterns: Observer
  21. Design patterns: State, cos’è e come usarlo (esempio PHP)
  22. Design patterns: Strategy
  23. Design patterns: Template Method
  24. Design patterns: Visitor
tutta la guida >

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