Modèle de conception de visiteur en Java

Modèle de conception de visiteur en Java

1. Vue d'ensemble

Dans ce didacticiel, nous présenterons l'un des modèles de conception comportementaux du GoF: le visiteur.

Tout d'abord, nous expliquerons son objectif et le problème qu'il tente de résoudre.

Ensuite, nous examinerons le diagramme UML du visiteur et la mise en œuvre de l'exemple pratique.

2. Modèle de conception visiteur

Le motif d'un visiteur est de définir une nouvelle opération sans introduire les modifications dans une structure d'objet existante.

Imaginons que nous ayons un lien: / java-composite-pattern object qui se compose de composants. La structure de l'objet est fixe - soit nous ne pouvons pas la modifier, soit nous ne prévoyons pas d'ajouter de nouveaux types d'éléments à la structure.

Maintenant, comment pourrions-nous ajouter de nouvelles fonctionnalités à notre code sans modifier les classes existantes?

Le modèle de conception Visiteur pourrait être une réponse. En termes simples,we’ll have to do is to add a function which accepts the visitor class to each element of the structure.

De cette manière, nos composants permettront à l’installation de visiteur de les «visiter» et d’exécuter toute action requise sur cet élément.

En d’autres termes, nous allons extraire l’algorithme qui sera appliqué à la structure de l’objet à partir des classes.

Par conséquent,we’ll make good use of the Open/Closed principle as nous ne modifierons pas le code, mais nous serons toujours en mesure d'étendre la fonctionnalité en fournissant une nouvelleVisitor implementation.

3. Diagramme UML

image

Sur le diagramme UML ci-dessus, nous avons deux hiérarchies d'implémentation, des visiteurs spécialisés et des éléments concrets.

Tout d'abord, le client utilise une implémentation Visiteur et l'applique à la structure d'objet. L'objet composite itère sur ses composants et applique le visiteur à chacun d'eux.

Maintenant, ce qui est particulièrement pertinent est queconcrete elements (ConcreteElementA and ConcreteElementB) are accepting a Visitor, simply allowing it to visit them.

Enfin, cette méthode est la même pour tous les éléments de la structure, elle effectuedouble dispatch en se passant (via le mot-cléthis) à la méthode de visite du visiteur.

4. la mise en oeuvre

Notre exemple sera le sobjectDocument personnalisé qui se compose d'éléments concrets JSON et XML; les éléments ont une superclasse abstraite commune, lesElement.

La classeDocument:

public class Document extends Element {

    List elements = new ArrayList<>();

    // ...

    @Override
    public void accept(Visitor v) {
        for (Element e : this.elements) {
            e.accept(v);
        }
    }
}

La classeElement a une méthode abstraite qui accepte l'interfaceVisitor :

public abstract void accept(Visitor v);

Par conséquent, lors de la création du nouvel élément, nommez-leJsonElement, nous devrons fournir l'implémentation de cette méthode.

Cependant, en raison de la nature du modèle de visiteur, la mise en œuvre sera la même. Dans la plupart des cas, cela nous obligerait à copier-coller le code standard à partir d'un autre élément déjà existant:

public class JsonElement extends Element {

    // ...

    public void accept(Visitor v) {
        v.visit(this);
    }
}

Étant donné que nos éléments permettent de les visiter par n'importe quel visiteur, disons que nous voulons traiter nosDocument élections, mais chacun d'eux d'une manière différente, en fonction de son type de classe.

Par conséquent, notre visiteur aura une méthode distincte pour le type donné:

public class ElementVisitor implements Visitor {

    @Override
    public void visit(XmlElement xe) {
        System.out.println(
          "processing an XML element with uuid: " + xe.uuid);
    }

    @Override
    public void visit(JsonElement je) {
        System.out.println(
          "processing a JSON element with uuid: " + je.uuid);
    }
}

Ici, notre visiteur concret implémente deux méthodes, respectivement une pour chaque type deElement.

Cela nous donne accès à l'objet particulier de la structure sur lequel nous pouvons effectuer les actions nécessaires.

5. Essai

À des fins de test, jetons un coup d'œil àVisitorDemoclass:

public class VisitorDemo {

    public static void main(String[] args) {

        Visitor v = new ElementVisitor();

        Document d = new Document(generateUuid());
        d.elements.add(new JsonElement(generateUuid()));
        d.elements.add(new JsonElement(generateUuid()));
        d.elements.add(new XmlElement(generateUuid()));

        d.accept(v);
    }

    // ...
}

Tout d'abord, nous créons unElementVisitor, il contient l'algorithme que nous appliquerons à nos éléments.

Ensuite, nous configurons notreDocument w avec les composants appropriés et appliquons le visiteur qui sera accepté par chaque élément d'une structure d'objet.

Le résultat serait comme ceci:

processing a JSON element with uuid: fdbc75d0-5067-49df-9567-239f38f01b04
processing a JSON element with uuid: 81e6c856-ddaf-43d5-aec5-8ef977d3745e
processing an XML element with uuid: 091bfcb8-2c68-491a-9308-4ada2687e203

Il montre que le visiteur a visité chaque élément de notre structure, en fonction du stypeElement , il a expédié le traitement à la méthode appropriée et pourrait récupérer les données de chaque objet sous-jacent.

6. Inconvénients

Comme chaque modèle de conception, même le visiteur a ses inconvénients, en particulier, son utilisation makes it more difficult to maintain the code if we need to add new elements to the object’s structure.

Par exemple, si nous ajoutons de nouveauxYamlElement,, nous devons mettre à jour tous les visiteurs existants avec la nouvelle méthode souhaitée pour traiter cet élément. Par la suite, si nous avons au moins dix visiteurs concrets, il peut être difficile de tous les mettre à jour.

En dehors de cela, lors de l'utilisation de ce modèle, la logique métier associée à un objet particulier est répartie sur toutes les implémentations de visiteurs.

7. Conclusion

Le modèle de visiteur est idéal pour séparer l’algorithme des classes sur lesquelles il opère. De plus, cela facilite l'ajout d'une nouvelle opération, simplement en fournissant une nouvelle implémentation du visiteur.

De plus, nous ne dépendons pas des interfaces des composants, et s’ils sont différents, c’est très bien, car nous avons un algorithme séparé pour le traitement par élément concret.

De plus, le visiteur peut éventuellement agréger des données en fonction de l'élément traversé.

Pour voir une version plus spécialisée du modèle de conception Visiteur, consultezvisitor pattern in Java NIO - l'utilisation du modèle dans le JDK.

Comme d'habitude, le code complet est disponible sur lesGithub project.