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.
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
UML class diagram
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.
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».
sous-répertoires
et fichiers
au répertoire
de l'entrée racinerépertoire
au fichier
et assurez-vous qu'il échoue$ 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
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
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
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
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
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)
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 classe
Directoryremplit ce rôle. (Deux rôles par personne) La classe
Directory de l'exemple de programme fournit ʻiterator
pour que le rôle ConcreteVisitor
puisse gérer les rôles ʻElement` individuels.
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()
Ajouter une classe d'exception
visitor/element.py
class FileTreatmentException(Exception):
def __init__(self,*args,**kwargs):
self.message = "FileTreatmentException"
Recommended Posts