Design patterns: Visitor

data: · 3 min
autore: Mauto
serie: Design Patterns/ parte 24/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 Visitor Pattern è un pattern comportamentale che consente di separare un algoritmo dalla struttura di oggetti su cui opera. Questo pattern permette di aggiungere nuove operazioni su un insieme di oggetti senza modificarne le classi. Viene definita una classe Visitor che rappresenta un’operazione da eseguire sugli elementi della struttura, e ogni elemento della struttura accetta un visitatore che esegue l’operazione appropriata.

Punti di Forza

  1. Aggiunta di nuove operazioni senza modificare le classi esistenti: Il pattern consente di estendere le funzionalità di una struttura di classi senza toccarne il codice, separando l’algoritmo dalla struttura.
  2. Singola responsabilità: Permette di separare le operazioni dalla struttura dati, rispettando il principio della singola responsabilità.
  3. Facile aggiunta di nuove operazioni: Le operazioni possono essere aggiunte creando nuovi visitatori, senza modificare le classi degli elementi.
  4. Rimozione del codice duplicato: Consente di centralizzare la logica comune all’interno dei visitatori, eliminando la duplicazione del codice tra gli elementi.
  5. Compatibilità con classi eterogenee: Funziona bene con strutture di oggetti complessi e con classi che non hanno una relazione gerarchica comune.

Caso d’uso: Report di struttura aziendale

Immagina di avere una struttura aziendale con vari ruoli (Manager, Developer) e desideri eseguire operazioni diverse su ciascun ruolo (come calcolare i bonus o generare report). Il pattern Visitor consente di eseguire queste operazioni separatamente dalle classi dei ruoli stessi.

Codice PHP

// Interfaccia per gli elementi che possono essere visitati
interface Employee {
    public function accept(EmployeeVisitor $visitor);
}

// Classe concreta per il Manager
class Manager implements Employee {
    private $name;
    private $salary;

    public function __construct($name, $salary) {
        $this->name = $name;
        $this->salary = $salary;
    }

    public function getName() {
        return $this->name;
    }

    public function getSalary() {
        return $this->salary;
    }

    // Accetta il visitatore
    public function accept(EmployeeVisitor $visitor) {
        $visitor->visitManager($this);
    }
}

// Classe concreta per il Developer
class Developer implements Employee {
    private $name;
    private $salary;

    public function __construct($name, $salary) {
        $this->name = $name;
        $this->salary = $salary;
    }

    public function getName() {
        return $this->name;
    }

    public function getSalary() {
        return $this->salary;
    }

    // Accetta il visitatore
    public function accept(EmployeeVisitor $visitor) {
        $visitor->visitDeveloper($this);
    }
}

// Interfaccia per i visitatori
interface EmployeeVisitor {
    public function visitManager(Manager $manager);
    public function visitDeveloper(Developer $developer);
}

// Visitor concreto che calcola il bonus
class BonusCalculatorVisitor implements EmployeeVisitor {
    public function visitManager(Manager $manager) {
        $bonus = $manager->getSalary() * 0.10;
        echo "Bonus per il Manager {$manager->getName()}: $bonus\n";
    }

    public function visitDeveloper(Developer $developer) {
        $bonus = $developer->getSalary() * 0.05;
        echo "Bonus per il Developer {$developer->getName()}: $bonus\n";
    }
}

// Visitor concreto che genera report
class ReportGeneratorVisitor implements EmployeeVisitor {
    public function visitManager(Manager $manager) {
        echo "Generazione report per il Manager: {$manager->getName()} con salario: {$manager->getSalary()}\n";
    }

    public function visitDeveloper(Developer $developer) {
        echo "Generazione report per il Developer: {$developer->getName()} con salario: {$developer->getSalary()}\n";
    }
}

// Esempio d'uso
$employees = [
    new Manager("Mario Rossi", 5000),
    new Developer("Luca Verdi", 3000)
];

// Creazione del visitatore per calcolare i bonus
$bonusVisitor = new BonusCalculatorVisitor();
foreach ($employees as $employee) {
    $employee->accept($bonusVisitor);
}

echo "\n";

// Creazione del visitatore per generare i report
$reportVisitor = new ReportGeneratorVisitor();
foreach ($employees as $employee) {
    $employee->accept($reportVisitor);
}

Spiegazione

  • Interfaccia Employee: Definisce il metodo accept() che accetta un visitatore.
  • Classi Manager e Developer: Implementano l’interfaccia Employee e implementano il metodo accept() per accettare un visitatore.
  • Interfaccia EmployeeVisitor: Definisce i metodi per visitare ogni tipo di oggetto (Manager e Developer).
  • Classi BonusCalculatorVisitor e ReportGeneratorVisitor: Implementano l’interfaccia EmployeeVisitor e definiscono le operazioni specifiche per ogni tipo di oggetto.
  • Uso del pattern Visitor: Il metodo accept() permette di applicare il visitatore sugli elementi della struttura, eseguendo le operazioni specifiche senza modificare le classi degli oggetti.

Conclusione

Il Visitor Pattern consente di aggiungere nuove operazioni a una struttura di oggetti complessa senza modificarne le classi. È utile quando si desidera mantenere il codice di operazioni separato dalle classi dei dati, garantendo estensibilità e flessibilità.

TORNA ALLA GUIDA

Copiato