Cosa si cela dietro al Visitor design pattern?
La programmazione orientata agli oggetti (OOP), che viene classificata come sottosezione del paradigma di programmazione imperativo ha assunto molta importanza negli ultimi anni. L’opzione di descrivere tutte le componenti di un progetto software come oggetto, il cui comportamento può essere definito tramite le relative classi, offre dei vantaggi decisivi rispetto ad altri stili di programmazione. Soprattutto la possibilità di poter riutilizzare componenti di programma senza alcun problema è un argomento decisivo che porta molti sviluppatori a decidere di utilizzare la OOP.
Per rendere ancora più semplice questa riusabilità, così come l’implementazione e la possibilità di effettuare dei test degli oggetti incorporati, i modelli di progettazione GoF sono stati presentati nel libro “Design Patterns: Elements of Reusable Object Oriented Software”. Tra questi oltre 20 design pattern troviamo anche il cosiddetto Visitor pattern o Visitor design pattern (in italiano: schema progettuale di visitatore), che verrà dettagliatamente descritto nei seguenti paragrafi.
Cos’è il Visitor pattern?
Il Visitor design pattern, in breve Visitor pattern, rappresenta un modello risolutivo per separare un algoritmo dalla struttura di oggetti a cui è applicato. Descrive un modo per inserire nuove operazioni in strutture di oggetti preesistenti, senza che queste strutture debbano essere modificate per questo. Grazie a questa proprietà, il Visitor pattern rappresenta una possibile opzione per l’applicazione del principio aperto/chiuso (OCP). Questo principio dello sviluppo software orientato agli oggetti si basa sul fatto che le unità software, come moduli, classi o metodi, sono contemporaneamente aperte (open) per delle estensioni e chiuse (closed) alle modifiche.
Il Visitor pattern è uno dei 23 modelli di progettazione (categoria: modello comportamentale descritti e pubblicati nel 1994 dagli informatici Erich Gamma, Richard Helm, Ralph Johnson e John Vlissides. Dato che i quattro sono conosciuti nel mondo degli sviluppatori anche come “Gang of Four”, abbreviato in GoF, per il suddetto pattern è stato coniato il nome di schema progettuale GoF.
Qual è lo scopo del Visitor design pattern?
Se la struttura a oggetti è composta da molte classi slegate tra loro con una costante richiesta di nuove operazioni, è difficile per lo sviluppatore dover implementare una nuova sottoclasse per ogni nuova operazione. Il risultato è un sistema con diverse classi di nodi, difficile non solo da capire ma anche da mantenere e modificare. L’istanza decisiva del Visitor pattern, il visitatore (visitor), permette di aggiungere nuove funzioni virtuali a una famiglia di classi, senza dover modificare queste ultime.
Le funzioni virtuali o metodi definiscono le funzioni obiettivo eseguibili, per le quali non è necessario che l’obiettivo sia già conosciuto al momento della compilazione. Rappresentano uno strumento importante della lingua orientata agli oggetti.
Lo schema progettuale di visitatore prevede che un tale oggetto visitatore abbia una definizione separata, con l’obiettivo di implementare un’operazione eseguita su un elemento o più elementi della struttura di oggetti. I client che hanno accesso alla struttura di oggetti richiamano il relativo metodo di elementi come “accept(visitor)”, che delegano la richiesta all’oggetto visitatore accettato. Ne consegue che l’oggetto visitatore può eseguire la relativa operazione.
Rappresentazione grafica del Visitor pattern (diagramma UML)
L’interazione tra gli elementi dati e gli oggetti visitatori inclusi secondo il Visitor design pattern può essere spiegata al meglio con una rappresentazione grafica delle relazioni e delle procedure di un possibile software orientato agli oggetti. A tal fine è perfetto il linguaggio di modellazione UML (Unified Modeling Language), che per questo motivo è stato utilizzato anche nel seguente diagramma di classi per il Visitor pattern.
I vantaggi e gli svantaggi del Visitor pattern
Il Visitor pattern rappresenta una via già collaudata e ben funzionante per estendere delle unità esistenti di un software orientato agli oggetti. In caso si debba aggiungere una nuova operazione, si può procedere tranquillamente tramite la definizione di un nuovo visitatore. Questo modo di procedere consente inoltre di centralizzare ogni codice funzionale: la relativa implementazione di un’operazione si trova centralmente nella classe visitatore e non deve essere completata in aggiunta nelle altre singole classi. Il vantaggio chiave di un software con il Visitor design pattern insomma, sta nel fatto che il codice sorgente alla base degli oggetti utilizzati non deve essere costantemente adeguato. La logica si suddivide invece tra il visitatore, che agisce come sostituto, e le classi di visitatori.
Naturalmente anche lo schema progettuale di visitatore non è perfetto in ogni suo punto. Chi lavora secondo i principi di questo modello deve essere consapevole che anche a seguito di minime modifiche alla classe di un elemento, nella maggior parte dei casi sono necessari adeguamenti anche nelle classi di visitatori attribuite. Inoltre, non si risparmia lavoro aggiuntivo per la successiva introduzione di nuovi elementi, perché anche per questi vanno implementati metodi visitatore, che a loro volta si devono integrare nelle classi ConcreteVisitor. L’eccellente possibilità di ampliare le unità di software è legata ad un certo impegno.
Dove si utilizza il Visitor pattern?
Il Visitor design pattern può semplificare notevolmente i compiti ricorrenti nello sviluppo di software. Soprattutto per gli sviluppatori che seguono il paradigma di programmazione orientato agli oggetti, sarebbe utile confrontarsi con questo schema progettuale. Dalla sua presentazione nel 1994, il modello si è affermato nell’ambiente di programmazione, e il tipo di progetto software non svolge un ruolo decisivo in linea di massima per il fattore d’uso del pattern. Anche relativamente ai linguaggi di programmazione che ne traggono vantaggio, non ci sono limitazioni concrete di base per il principio di modellazione, a meno che sia stato tarato in modo particolare per il paradigma orientato agli oggetti.
Il Visitor pattern svolge un ruolo elementare tra gli altri nei seguenti popolari linguaggi di programmazione:
- C++
- C#
- Java
- PHP
- Python
- JavaScript
- Golang
Esempi pratici dell’impiego del Visitor pattern
Non è per niente semplice capire i vantaggi e lo scopo del Visitor pattern per un osservatore esterno. Ma chi studia programmazione viene automaticamente a contatto con la modellazione e la sua applicazione.
Per creare un’analogia con la vita reale, per il Visitor pattern si utilizza spesso l’esempio di un viaggio in taxi: un cliente prenota un taxi che arriva, su richiesta, fino alla porta di casa sua. Una volta che la persona è seduta all’interno del taxi “in visita”, quest’ultimo (o il tassista) è completamente responsabile del trasporto della persona.
Spesso per illustrare il funzionamento del Visitor pattern si usa anche l’immagine della spesa al supermercato: la persona che deve effettuare gli acquisti mette nel carrello ciò che desidera comprare, che metaforicamente rappresenta il set di elementi della struttura di oggetti. Una volta alla cassa, il cassiere è il visitatore, che scansiona i prezzi e il peso dei singoli prodotti (o elementi) scelti, per calcolare il costo finale.
Esempio di codice secondo l’approccio Visitor pattern (PHP)
In ultimo vi presentiamo il seguente codice, una semplice applicazione del Visitor pattern in PHP.
return 'B';
}
public function getData() {
return $this->the_data;
}
publicfunction accettare(Visitatore $visitatore) {
$visitatore->VisitaDiElementoB($this);
}
}
abstract class visitatore {
abstract function VisitaDiElementoA(ElementoA $elem);
abstract function VisitaDiElementoB(ElementoB $elem);
}
class Visitatore1 extends Visitatore {
private $characteristics;
public function getCharacs() {
return $this->characteristics;
}
public function VisitaDiElementoA(ElementoA $elem) {
$this->characteristics = 'Info:'.$elem->getInfo();
}
abstract function VisitaDiElementoB(ElementoB $elem){
$this->characteristics = 'DATA:'.$elem->getData().'!!';
}
}
function Test() {
write_line('InizioTesto');
// Struttura di oggetti
$elementi = array (
newElementoA('Ciao', 'Nuovo!!'),
newElementoB('Finalmente.'),
);
$bes1 = nuovo visitatore1();
foreach ($elementi as $elemento) {
$elemento->accettare($bes1);
write_line('Dopo la visita di elemento '.$elemento->getName().': '.$bes1- >getCharacs());
}
}
functionwrite_line($text) {
print $text.'<br>';
}
Test();
L’output di questo snippet di codice esempio appare come segue:
Inizio testo
Dopo la visita di elemento A: Info:[Ciao--Nuovo!!]
Dopo la visita di elemento B: DATA:(Finalmente.)!!