Visitor pattern : que recouvre le patron de conception Visitor ?

Ces dernières années, l’importance de la programmation orientée objet (OOP), une sous-forme du paradigme de programmation impérative, n’a cessé de croître. Par rapport à d’autres styles de programmation, l’option qui consiste à décrire tous les modules d’un projet logiciel comme un objet, dont le comportement peut être défini par des classes correspondantes, présente plusieurs avantages décisifs. Pour de nombreux développeurs, la possibilité de réutiliser facilement des parties du programme déjà conçues est un argument évident en faveur de l’utilisation d’une OOP.

Pour faciliter encore la réutilisabilité ainsi que l’implémentation, l’adaptabilité et la testabilité des objets intégrés, quatre informaticiens ont publié les patrons de conception GoF dans l’ouvrage « Design Patterns: Elements of Reusable Object-Oriented Software ». Parmi cette vingtaine de patrons de conception, on trouve le Visitor pattern ou Visitor design pattern (en français : patron de conception Visiteur) que nous détaillons pour vous dans les paragraphes suivants.

Qu’est-ce que le Visitor pattern ?

Le Visitor design pattern, abrégé en Visitor pattern, fournit une solution pour séparer un algorithme de la structure de l’objet sur lequel il travaille. Il décrit une méthode pour ajouter de nouvelles opérations à des structures d’objets existantes sans que ces structures aient besoin d’être modifiées. Grâce à cette caractéristique, le Visitor pattern peut être utilisé pour mettre en œuvre le principe ouvert/fermé. Ce principe du développement logiciel orienté objet repose sur le fait que chaque unité d’un logiciel – telles que les modules, les classes ou les méthodes – est à la fois ouverte (open) aux extensions et fermée (closed) aux modifications.

Note

En français, on désigne également le Visitor Pattern sous les termes « patron de conception Visiteur » ou « patron Visiteur ».

Le Visitor pattern est l’un des 23 patrons de conception (catégorie : patron comportemental) décrits et publiés en 1994 par les informaticiens Erich Gamma, Richard Helm, Ralph Johnson et John Vlissides. Ces quatre informaticiens étant connus sous le nom de « Gang of Four » (fr. bande de quatre ; GoF), l’appellation patrons de conception GoF s’est établie pour désigner ces design patterns.

À quoi sert le Visitor pattern ?

Lorsque la structure de l’objet est composée de nombreuses classes et que de nouvelles opérations sont souvent nécessaires, il est très laborieux pour les développeurs de devoir implémenter une nouvelle sous-classe pour chaque nouvelle opération. En procédant de la sorte, le système obtenu disposerait de différentes classes de nœud qui seraient non seulement trop compliquées à comprendre mais aussi à entretenir et à modifier. L’instance décisive du Visitor pattern, le visiteur (Visitor), permet d’ajouter de nouvelles fonctionnalités virtuelles à une famille de classes sans avoir à les modifier.

Note

Les fonctionnalités ou les méthodes virtuelles définissent les fonctionnalités cibles à exécuter dont le but n’a pas à être connu au moment de la compilation. Il s’agit d’un outil essentiel des langages orientés objet.

Le patron de conception Visiteur prévoit une définition séparée de l’objet visiteur dans l’idée d’implémenter une opération qui sera exécutée sur un ou plusieurs éléments de la structure de l’objet. Les clients accédant à la structure de l’objet appellent ensuite des méthodes (par ex. « accept(visitor) ») sur l’élément concerné. Ces méthodes délèguent la requête à l’objet visiteur accepté qui peut ensuite exécuter l’opération concernée.

Représentation graphique du Visitor pattern (diagramme UML)

La façon la plus simple d’illustrer l’interaction entre les éléments existants et les objets visiteurs intégrés selon le Visitor pattern est d’utiliser une représentation graphique des relations et des processus d’un potentiel logiciel orienté objet. Très apprécié, le langage de modélisation UML (Unified Modeling Language) est idéal pour cela. C’est la raison pour laquelle nous l’avons utilisé dans le diagramme de classes suivant représentant le Visitor pattern.

Avantages et inconvénients du Visitor pattern

Le Visitor pattern constitue un moyen éprouvé et efficace d’étendre les unités existantes d’un logiciel orienté objet. Une nouvelle opération peut facilement être ajoutée en définissant un nouveau visiteur. Cette méthode permet d’ailleurs également de centraliser le code fonctionnel : l’implémentation correspondant à une opération est centralisée dans la classe visiteur et n’a pas à être complétée dans les autres classes individuelles. Pour faire simple, l’avantage décisif d’un logiciel conçu selon le Visitor pattern réside dans le fait que le code source des objets utilisés n’a pas à être constamment ajusté. À la place, la logique est répartie entre les visiteurs, agissant comme des remplaçants, et les classes de visiteurs.

Il va de soi que le patron de conception Visiteur n’est pas irréprochable. En adoptant les principes de ce patron, les développeurs doivent être conscients des éléments suivants : toutes les modifications apportées à la classe d’un élément, même les plus petites, entraînent généralement un ajustement des classes visiteurs affectées. Par ailleurs, ce patron n’évite pas le travail supplémentaire nécessaire pour introduire de nouveaux éléments a posteriori puisqu’il faudra également implémenter pour ces éléments des méthodes visit() qui devront à leur tour être ajoutées dans les classes ConcreteVisitor. La remarquable modularité des unités logicielles n’est donc pas obtenue sans efforts.

Dans quel cas le Visitor pattern est-il utilisé ?

Le Visitor pattern peut considérablement simplifier les tâches récurrentes en développement logiciel. Il est intéressant de se pencher de plus près sur ce patron de conception, en particulier pour les développeurs suivant le paradigme de la programmation orientée objet. Depuis sa présentation en 1994, ce patron est parvenu à s’imposer dans le milieu de la programmation, le type de projet logiciel ne jouant en principe aucun rôle décisif dans l’utilité de ce patron. En ce qui concerne les langages de programmation pouvant en bénéficier, il n’existe en principe aucune restriction concrète pour ce patron, si ce n’est qu’il est tout particulièrement adapté au paradigme orienté objet.

Le Visitor pattern joue notamment un rôle fondamental dans les langages suivants :

  • C++
  • C#
  • Java
  • PHP
  • Python
  • JavaScript
  • Golang
Pour afficher cette vidéo, des cookies de tiers sont nécessaires. Vous pouvez consulter et modifier vos paramètres de cookies ici.

Exemple pratique de l’utilisation du Visitor pattern

D’un point de vue extérieur, il n’est pas toujours simple de comprendre l’utilisation et l’utilité du Visitor pattern. De nos jours, toute personne souhaitant apprendre la programmation sera pourtant confrontée à ce patron et à sa mise en œuvre.

Pour expliquer le patron de conception Visiteur, on utilise souvent une analogie facile à comprendre, celle du trajet en taxi : un client réserve un taxi qui peut venir le prendre au pas de sa porte. Lorsque la personne est assise dans le taxi, « qui lui rend visite », le taxi (ou son conducteur) dispose d’un contrôle total sur le transport de la personne.

L’analogie des achats dans un supermarché est elle aussi souvent utilisée pour expliquer le fonctionnement du Visitor pattern : dans son caddy, la personne réalisant les achats rassemble les articles qu’elle désire. Ces achats représentent l’ensemble des éléments de la structure de l’objet. Une fois à la caisse, le caissier tient le rôle du visiteur scannant les prix et le poids des différents articles (ou éléments) afin de calculer le coût total à payer.

Exemple de code selon l’approche du Visitor pattern (PHP)

Pour finir, le code suivant présente une mise en œuvre simple et basique du Visitor pattern en PHP.

        return ‘B’;
	}
	public function getData() {
		return $this->the_data;
	}
	public function accepter(visiteur $visiteur) {
		$visiteur->VisiteDeLElementB($this);
	}
}
abstract class visiteur {
	abstract function VisiteDeLElementA(ElementA $elem);
	abstract function VisiteDeLElementB(ElementB $elem);
}
class visiteur1 extends visiteur{
	private $characteristics;
	public function getCharacs() {
		return $this->characteristics;
	}
	public function VisiteDeLElementA(ElementA $elem) {
		$this->characteristics = ‘Info:’.$elem->getInfo();
	}
	public function VisiteDeLElementB(ElementB $elem) {
		$this->characteristics = ‘DATA:’.$elem->getData().’!!’;
	}
}
function Test() {
	write_line(‘début du test’);
	// Structure de l’objet
	$elemente = array (
		new ElementA(‘Bonjour’, ‘Nouveau !!’),
		new ElementB(‘Enfin.’),
	);
	$bes1 = new visiteur1();
	foreach ($elemente as $element) {
		$element->accepter($vis1);
		write_line(‘Après la visite de l’élément’.$element->getName().’: ‘.$bes1-			>getCharacs());
}
}
function write_line($text) {
	print $text.’<br>‘;
}
Test();

L’extrait de code ressemble alors à ce qui suit :

Début du test
Après la visite de l’élément A : Info:[Bonjour--Nouveau !!]
Après la visite de l’élément B : DATA:(Enfin.)!!