[AST] Je recherche dans (http://ja.wikipedia.org/wiki/%E6%8A%BD%E8%B1%A1%E6%A7%8B%E6%96%87%E6%9C%A8) [Visiteur modèle] (http://ja.wikipedia.org/wiki/Visitor_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3) est sorti. Qu'est-ce qui vous a rendu heureux lorsque vous avez utilisé le modèle Visiteur? Je ne connaissais pas les bases, alors je l'ai revu: sueur:
wikipedia a un exemple de code Java, donc je l'ai réécrit en Python3. Au départ, je portais le code japonais en regardant l'exemple, et si je pensais que c'était un échantillon à moitié fini, le [modèle de visiteur] anglais (http://en.wikipedia.org/wiki/Visitor_pattern) ) A été modifié en un exemple de code plus concis. Il semble préférable de regarder cette page en anglais.
Python n'a pas d'interface, j'utilise donc le décorateur @ abstractmethod
du module abc. En passant, le module * abc * ne s'applique qu'aux instances de classes, mais * zope.interface * est idéal pour les classes, les objets, les modules, etc.
Pause de conversation tranquille. Regardons le modèle tout en regardant le code.
3.4
# -*- coding: utf-8 -*-
from abc import ABCMeta, abstractmethod
class ICarElementVisitor(metaclass=ABCMeta):
"""
Interface like in Python
"""
@abstractmethod
def visit_Wheel(self, wheel): pass
@abstractmethod
def visit_Engine(self, engine): pass
@abstractmethod
def visit_Body(self, body): pass
@abstractmethod
def visit_Car(self, car): pass
class ICarElement(metaclass=ABCMeta):
"""
Interface like in Python
"""
@abstractmethod
def accept(self, visitor): pass
class Wheel(ICarElement):
def __init__(self, name):
self.name = name
def accept(self, visitor):
visitor.visit_Wheel(self)
class Engine(ICarElement):
def accept(self, visitor):
visitor.visit_Engine(self)
class Body(ICarElement):
def accept(self, visitor):
visitor.visit_Body(self)
class Car(ICarElement):
def __init__(self):
self.elements = [
Wheel('front left'), Wheel('front right'),
Wheel('back left'), Wheel('back right'),
Body(), Engine(),
]
def accept(self, visitor):
for elem in self.elements:
elem.accept(visitor)
visitor.visit_Car(self)
class PrintVisitor(ICarElementVisitor):
def visit_Wheel(self, wheel):
print('Visiting {} wheel'.format(wheel.name))
def visit_Engine(self, engine):
print('Visiting engine')
def visit_Body(self, body):
print('Visiting body')
def visit_Car(self, car):
print('Visiting car')
class DoVisitor(ICarElementVisitor):
def visit_Wheel(self, wheel):
print('Kicking my {} wheel'.format(wheel.name))
def visit_Engine(self, engine):
print('Starting my engine')
def visit_Body(self, body):
print('Moving my body')
def visit_Car(self, car):
print('Starting my car')
def main():
"""
>>> main()
Visiting front left wheel
Visiting front right wheel
Visiting back left wheel
Visiting back right wheel
Visiting body
Visiting engine
Visiting car
--------------------------------
Kicking my front left wheel
Kicking my front right wheel
Kicking my back left wheel
Kicking my back right wheel
Moving my body
Starting my engine
Starting my car
"""
car = Car()
car.accept(PrintVisitor())
print('-' * 32)
car.accept(DoVisitor())
Si vous portez le code Java tel quel, il ressemble à ceci, mais comme il s'agit de Python, lâchons-le un peu plus.
--- visitor.py 2015-02-17 18:43:53.000000000 +0900
+++ visitor-generic.py 2015-02-17 18:46:24.000000000 +0900
@@ -6,16 +6,8 @@
Interface like in Python
"""
@abstractmethod
- def visit_Wheel(self, wheel): pass
-
- @abstractmethod
- def visit_Engine(self, engine): pass
-
- @abstractmethod
- def visit_Body(self, body): pass
-
- @abstractmethod
- def visit_Car(self, car): pass
+ def visit(self, obj):
+ getattr(self, 'visit_' + obj.__class__.__name__)(obj)
class ICarElement(metaclass=ABCMeta):
"""
@@ -29,15 +21,15 @@
self.name = name
def accept(self, visitor):
- visitor.visit_Wheel(self)
+ visitor.visit(self)
class Engine(ICarElement):
def accept(self, visitor):
- visitor.visit_Engine(self)
+ visitor.visit(self)
class Body(ICarElement):
def accept(self, visitor):
- visitor.visit_Body(self)
+ visitor.visit(self)
class Car(ICarElement):
@@ -51,9 +43,12 @@
def accept(self, visitor):
for elem in self.elements:
elem.accept(visitor)
- visitor.visit_Car(self)
+ visitor.visit(self)
class PrintVisitor(ICarElementVisitor):
+ def visit(self, obj):
+ getattr(self, 'visit_' + obj.__class__.__name__)(obj)
+
def visit_Wheel(self, wheel):
print('Visiting {} wheel'.format(wheel.name))
@@ -67,6 +62,9 @@
print('Visiting car')
class DoVisitor(ICarElementVisitor):
+ def visit(self, obj):
+ super().visit(obj)
+
Pour expliquer brièvement les changements, nous utilisons la réflexion pour définir une implémentation par défaut pour quelque chose de type interface.
3.4
class ICarElementVisitor(metaclass=ABCMeta):
"""
Interface like in Python
"""
@abstractmethod
def visit(self, obj):
getattr(self, 'visit_' + obj.__class__.__name__)(obj)
Ensuite, la classe qui reçoit le visiteur peut être unifiée pour appeler visiteur.visit (self)
, ce qui est un peu plus propre.
3.4
class Engine(ICarElement):
def accept(self, visitor):
visitor.visit(self)
La sous-classe Visiteur peut définir directement une implémentation de «visit ()», telle que «PrintVisitor», ou elle peut utiliser l'implémentation par défaut d'une classe abstraite (interface), telle que «DoVisitor».
3.4
class PrintVisitor(ICarElementVisitor):
def visit(self, obj):
getattr(self, 'visit_' + obj.__class__.__name__)(obj)
...
class DoVisitor(ICarElementVisitor):
def visit(self, obj):
super().visit(obj)
...
Le pattern Visitor passe l'objet Visitor à la méthode ʻaccept () et appelle la méthode
visiteur.visit (self) à l'intérieur ([Double Dispatch](http://en.wikipedia.org/wiki/Double_dispatch)). ). Cet exemple appelle également la méthode ʻelem.accept (visiteur)
de chaque objet élément à l'intérieur decar.accept (visiteur)
.
L'un des avantages est que vous pouvez opérer sur plusieurs objets élément (* traverse *) afin de ne pas avoir à modifier le code ou la structure de données des objets existants lorsque vous ajoutez de nouvelles opérations. Cela suscite des inquiétudes (http://en.wikipedia.org/wiki/Separation_of_concerns).
Par exemple, si vous pensez ajouter une opération appelée freinage,
+class Brake(ICarElement):
+ def accept(self, visitor):
+ visitor.visit(self)
+
class Car(ICarElement):
def __init__(self):
self.elements = [
Wheel('front left'), Wheel('front right'),
Wheel('back left'), Wheel('back right'),
- Body(), Engine(),
+ Body(), Engine(), Brake(),
]
@@ -55,6 +59,9 @@
def visit_Engine(self, engine):
print('Visiting engine')
+ def visit_Brake(self, engine):
+ print('Visiting brake')
+
C'est tout ce que vous devez faire.
De plus, l'objet visiteur
est décrit comme étant plus puissant que la méthode * polymorphe * car il peut aussi avoir des états.
Dans cet exemple, l'état n'est pas géré en particulier, mais on peut dire qu'il est facile d'enregistrer l'ordre dans lequel ils sont appelés, ou de mettre un drapeau lorsqu'une opération est effectuée.
Après l'avoir écrit et réexaminé, [Comparaison du modèle de visiteur et du typage du canard] Selon (http://d.hatena.ne.jp/podhmo/20101127/1290845198), [Expert Python Programming](http://ascii.asciimw.jp/books/books/detail/978-4-04- Était-il répertorié dans 868629-7.shtml)? C'était il y a de nombreuses années, et je me souviens avoir assisté à une séance de lecture et en avoir parlé là-bas, mais je ne me souvenais pas du tout du code.
Recommended Posts