Design patterns: Visitor
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
- 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.
- Singola responsabilità: Permette di separare le operazioni dalla struttura dati, rispettando il principio della singola responsabilità.
- Facile aggiunta di nuove operazioni: Le operazioni possono essere aggiunte creando nuovi visitatori, senza modificare le classi degli elementi.
- Rimozione del codice duplicato: Consente di centralizzare la logica comune all’interno dei visitatori, eliminando la duplicazione del codice tra gli elementi.
- 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);
}
// Visitator 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 sovrascrivono 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à.