Apprenez le modèle de conception "Visiteur" avec Python

En tant que matériel d'apprentissage des modèles de conception du GoF, le livre «Introduction aux modèles de conception appris dans le langage Java augmenté et révisé» semble être utile. Cependant, comme les exemples repris sont basés sur JAVA, j'ai essayé la même pratique avec Python pour approfondir ma compréhension.

■ Modèle de visiteur (modèle de visiteur)

Le modèle de visiteur est un modèle de conception pour séparer les algorithmes de la structure des objets dans la programmation orientée objet et le génie logiciel. Comme résultat pratique de la séparation, de nouvelles opérations sur des objets existants peuvent être ajoutées sans changer la structure. Fondamentalement, le modèle Visiteur vous permet d'ajouter de nouvelles fonctions virtuelles à un groupe de classes sans changer la classe elle-même. Pour ce faire, créez une classe Visiteur qui se spécialise correctement dans toutes les fonctions virtuelles. Le visiteur reçoit une référence à l'instance en tant qu'entrée et utilise la double distribution pour atteindre son objectif. Les visiteurs sont puissants, mais ils ont également des limites par rapport aux fonctions virtuelles existantes. Vous devez ajouter une petite méthode de rappel dans chaque classe, et la méthode de rappel de chaque classe ne peut pas être héritée par une nouvelle sous-classe.

UML class and sequence diagram W3sDesign_Visitor_Design_Pattern_UML.jpg

UML class diagram Visitor_design_pattern.svg.png

□ Mémorandum

Ceci est une citation du livre "Introduction aux modèles de conception appris en langage Java", mais j'avais faim.

Visiteur signifie «visiteur». Supposons que vous ayez beaucoup d'éléments stockés dans votre structure de données et que vous souhaitiez effectuer un "traitement" sur chacun d'eux. Où le code de «traitement» doit-il être écrit à ce moment? Si vous y réfléchissez normalement, vous l'écrivez dans une classe qui représente la structure des données. Mais que se passe-t-il si le «traitement» n'est pas toujours un type? Dans ce cas, la classe de structure de données devrait être modifiée chaque fois qu'un nouveau traitement est nécessaire. Le modèle «Visiteur» ** sépare la structure des données et le traitement **. Ensuite, préparez une classe qui représente le "visiteur" qui est le corps principal qui fait le tour de la structure de données et laissez cette classe gérer le traitement. Ensuite, lorsque vous souhaitez ajouter un nouveau processus, vous pouvez créer un nouveau "visiteur". Et la structure de données devrait accepter les «visiteurs» qui ont frappé la porte.

■ Exemple de programme "Visiteur"

En fait, je voudrais exécuter un exemple de programme qui utilise le modèle Visiteur et vérifier le comportement suivant. Au fait, le comportement est le même que celui de l'exemple de programme dans article Qiita "Apprenez le modèle de conception" Composite "avec Python", comparez donc les implémentations. Cela vous donnera une meilleure compréhension du modèle «Visiteur».

$ python Main.py 
Making root entries
/root (30000)
/root/bin (30000)
/root/bin/vi (10000)
/root/bin/latex (20000)
/root/tmp (0)
/root/usr (0)

Making user entries...
/root (31500)
/root/bin (30000)
/root/bin/vi (10000)
/root/bin/latex (20000)
/root/tmp (0)
/root/usr (1500)
/root/usr/yuki (300)
/root/usr/yuki/diary.html (100)
/root/usr/yuki/composite.py (200)
/root/usr/hanako (300)
/root/usr/hanako/memo.tex (300)
/root/usr/tomura (900)
/root/usr/tomura/game.doc (400)
/root/usr/tomura/junk.mail (500)

Occurring Exception...
FileTreatmentException

■ Détails de l'exemple de programme

Un code similaire a été téléchargé dans le référentiel Git. https://github.com/ttsubo/study_of_design_pattern/tree/master/Visitor

.
├── Main.py
└── visitor
    ├── __init__.py
    ├── element.py
    └── visitor.py

(1) Le rôle d'un visiteur

Le rôle Visiteur déclare une méthode visit (xxxx) qui dit" visité xxxx "pour chaque élément spécifique de la structure de données (rôle ConcreteElement). visit (xxxx) est une méthode de traitement de xxxx. Le code réel est écrit à côté du rôle Visiteur concret. Dans l'exemple de programme, la classe Visiteur remplit ce rôle.

visitor/visitor.py


from abc import ABCMeta, abstractmethod

class Vistor(metaclass=ABCMeta):
    @abstractmethod
    def visit(self, directory):
        pass

(2) Le rôle du visiteur concret

Le rôle ConcreteVisitor implémente l'interface pour le rôle Visiteur. Implémentez une méthode du formulaire visiteur (xxxx) et décrivez le traitement pour chaque rôle individuel ConcreteElement. Dans l'exemple de programme, la classe ListVistor remplit ce rôle.

visitor/visitor.py


class ListVistor(Vistor):
    def __init__(self):
        self.__currentdir = ''

    def visit(self, directory):
        print("{0}/{1}".format(self.__currentdir, directory))
        if isinstance(directory, Directory):
            savedir = self.__currentdir
            self.__currentdir = self.__currentdir + '/' + directory.getName()
            for f in directory:
                f.accept(self)
            self.__currentdir = savedir

(3) Rôle de l'élément

Le rôle de ʻElement est un rôle qui représente la destination du rôle de Visiteur. Déclarez la méthode «accept» pour accepter la visite. Le rôle Visiteur est passé à l'argument de la méthode ʻaccept. Dans l'exemple de programme, la classe ʻElement` remplit ce rôle.

visitor/element.py


from abc import ABCMeta, abstractmethod

class Element(metaclass=ABCMeta):
    @abstractmethod
    def accept(self, v):
        pass

(4) Rôle de ConcreteElement

Le rôle ConcreteElement est le rôle qui implémente l'interface pour le rôle ʻElement. Dans l'exemple de programme, la classe ʻEntry, la classe File et la classe Directory remplissent ce rôle.

visitor/element.py


class Entry(Element):
    @abstractmethod
    def getName(self):
        pass

    @abstractmethod
    def getSize(self):
        pass

    def add(self, entry):
        raise FileTreatmentException

    def __str__(self):
        return "{0} ({1})".format(self.getName(), self.getSize())


class File(Entry):
    def __init__(self, name, size):
        self.__name = name
        self.__size = size

    def getName(self):
        return self.__name

    def getSize(self):
        return self.__size

    def accept(self, v):
        v.visit(self)


class Directory(Entry):
    def __init__(self, name):
        self.__name = name
        self.__dir = []

    def getName(self):
        return self.__name

    def getSize(self):
        size = 0
        for f in self.__dir:
            size += f.getSize()
        return size

    def add(self, entry):
        self.__dir.append(entry)
        return self
    
    def __iter__(self):
        self.__index = 0
        return self

    def __next__(self):
        if self.__index >= len(self.__dir):
            raise StopIteration()
        dir = self.__dir[self.__index]
        self.__index += 1
        return dir

    def accept(self, v):
        v.visit(self)

(5) Le rôle de la structure d'objets

Le rôle ʻObject Structure est un rôle qui gère un ensemble de rôles ʻElement. Il a une méthode qui permet au rôle ConcreteVisitor de gérer des rôles ʻElementindividuels. Dans l'exemple de programme, la classeDirectoryremplit ce rôle. (Deux rôles par personne) La classeDirectory de l'exemple de programme fournit ʻiterator pour que le rôle ConcreteVisitor puisse gérer les rôles ʻElement` individuels.

(6) Le rôle du client

Dans l'exemple de programme, la méthode startMain remplit ce rôle.

Main.py


from visitor.visitor import ListVistor
from visitor.element import File, Directory, FileTreatmentException

def startMain():
    try:
        print("Making root entries")
        rootdir = Directory("root")
        bindir = Directory("bin")
        tmpdir = Directory("tmp")
        usrdir = Directory("usr")

        rootdir.add(bindir)
        rootdir.add(tmpdir)
        rootdir.add(usrdir)

        bindir.add(File("vi", 10000))
        bindir.add(File("latex", 20000))
        rootdir.accept(ListVistor())

        print("")

        print("Making user entries...")
        yuki = Directory("yuki")
        hanako = Directory("hanako")
        tomura = Directory("tomura")

        usrdir.add(yuki)
        usrdir.add(hanako)
        usrdir.add(tomura)

        yuki.add(File("diary.html", 100))
        yuki.add(File("composite.py", 200))
        hanako.add(File("memo.tex", 300))
        tomura.add(File("game.doc", 400))
        tomura.add(File("junk.mail", 500))
        rootdir.accept(ListVistor())

        print("")
        print("Occurring Exception...")
        tmpfile = File("tmp.txt", 100)
        bindir = Directory("bin")
        tmpfile.add(bindir)
    except FileTreatmentException as ex:
        print(ex.message)

if __name__ == '__main__':
    startMain()

(7) Autre

Ajouter une classe d'exception

visitor/element.py


class FileTreatmentException(Exception):
    def __init__(self,*args,**kwargs):
        self.message = "FileTreatmentException"

■ URL de référence

Recommended Posts

Apprenez le modèle de conception "Visiteur" avec Python
Apprenez le modèle de conception "Prototype" avec Python
Apprenez le modèle de conception "Builder" avec Python
Apprenez le modèle de conception "Flyweight" en Python
Apprenez le modèle de conception "Observer" en Python
Apprenez le modèle de conception "Memento" avec Python
Apprenez le modèle de conception "Proxy" en Python
Apprenez le modèle de conception "Commande" en Python
Apprenez le modèle de conception "Bridge" avec Python
Apprenez le modèle de conception "Mediator" avec Python
Apprenez le modèle de conception "Décorateur" avec Python
Apprenez le modèle de conception "Iterator" avec Python
Apprenez le modèle de conception «Stratégie» avec Python
Apprenez le modèle de conception "Composite" avec Python
Apprenez le modèle de conception "État" en Python
Apprenez le modèle de conception "Adapter" avec Python
Apprenez le modèle de conception "Abstract Factory" avec Python
Apprenez le modèle de conception "Méthode de modèle" en Python
Apprenez le modèle de conception "Méthode d'usine" en Python
Apprenez le modèle de conception «Chaîne de responsabilité» en Python
Modèle de visiteur en Python
Apprenez le modèle de conception "Singleton" avec Python
Apprenez le modèle de conception "Façade" avec Python
Implémenter le modèle Singleton en Python
Motif singleton en Python
À propos du modèle de visiteur
Trouver des erreurs en Python
Modèles de conception en Python: introduction
Python Design Pattern - Méthode de modèle
Python dans le navigateur: la recommandation de Brython
Enregistrez le fichier binaire en Python
Frappez l'API Sesami en Python
Obtenez le chemin du bureau en Python
Obtenez le chemin du script en Python
Dans la commande python, python pointe vers python3.8
Accédez à l'API Web en Python
J'ai écrit la file d'attente en Python
Calculer le mois précédent en Python
Examiner la classe d'un objet avec python
Obtenez le chemin du bureau en Python
Obtenez le nom d'hôte en Python
Accéder à l'API Twitter avec Python
La première étape de Python Matplotlib
J'ai écrit la pile en Python
Maîtriser le module lowref en Python
Apprenez les bases de Python ① Débutants élémentaires
Essayez d'utiliser l'API Wunderlist en Python
Vérifiez le comportement du destroyer en Python
[Python Kivy] À propos de la modification du thème de conception
[Gang of Four] Apprentissage des modèles de conception - Visiteur
Essayez d'utiliser l'API Kraken avec Python
Apprenez les bases en touchant les variables python
Ecrire le test dans la docstring python
Prenez la somme logique de List en Python (fonction zip)
Modèle de conception du GoF à partir du problème 2. Structure
Afficher Python 3 dans le navigateur avec MAMP
Tweet à l'aide de l'API Twitter en Python
Vérifiez si l'URL existe en Python