Als Material zum Erlernen von GoF-Entwurfsmustern scheint das Buch "Einführung in Entwurfsmuster, die in der erweiterten und überarbeiteten Java-Sprache gelernt wurden" hilfreich zu sein. Da die aufgeführten Beispiele jedoch auf JAVA basieren, habe ich dieselbe Vorgehensweise mit Python versucht, um mein Verständnis zu vertiefen.
Das Besuchermuster ist ein Entwurfsmuster zum Trennen von Algorithmen von der Struktur von Objekten in der objektorientierten Programmierung und Softwareentwicklung. Als praktisches Ergebnis der Trennung können neue Operationen an vorhandenen Objekten hinzugefügt werden, ohne die Struktur zu ändern. Grundsätzlich können Sie mit dem Besuchermuster einer Gruppe von Klassen neue virtuelle Funktionen hinzufügen, ohne die Klasse selbst zu ändern. Erstellen Sie dazu eine Besucherklasse, die sich ordnungsgemäß auf alle virtuellen Funktionen spezialisiert hat. Der Besucher nimmt einen Verweis auf die Instanz als Eingabe und verwendet den doppelten Versand, um sein Ziel zu erreichen. Besucher sind leistungsstark, haben aber auch Einschränkungen im Vergleich zu vorhandenen virtuellen Funktionen. Sie müssen innerhalb jeder Klasse eine kleine Rückrufmethode hinzufügen, und die Rückrufmethode jeder Klasse kann nicht von einer neuen Unterklasse geerbt werden.
UML class and sequence diagram
UML class diagram
Dies ist ein Zitat aus dem Buch "Einführung in Designmuster, die in der Java-Sprache gelernt wurden", aber ich hatte Hunger.
Besucher bedeutet "Besucher". Angenommen, Sie haben viele Elemente in Ihrer Datenstruktur gespeichert und möchten für jedes Element eine "Verarbeitung" durchführen. Wo soll der "Verarbeitungs" -Code zu diesem Zeitpunkt geschrieben werden? Wenn Sie normal darüber nachdenken, schreiben Sie es in eine Klasse, die die Datenstruktur darstellt. Aber was ist, wenn die "Verarbeitung" nicht immer ein Typ ist? In diesem Fall müsste die Datenstrukturklasse jedes Mal geändert werden, wenn eine neue Verarbeitung erforderlich wäre. Das Besuchermuster ** trennt Datenstruktur und Verarbeitung **. Bereiten Sie dann eine Klasse vor, die den "Besucher" darstellt, der der Hauptteil der Datenstruktur ist, und lassen Sie diese Klasse die Verarbeitung übernehmen. Wenn Sie dann einen neuen Prozess hinzufügen möchten, können Sie einen neuen "Besucher" erstellen. Und die Datenstruktur sollte die "Besucher" akzeptieren, die die Tür öffnen.
Eigentlich möchte ich ein Beispielprogramm ausführen, das das Besuchermuster verwendet, und das folgende Verhalten überprüfen. Das Verhalten ist übrigens das gleiche wie im Beispielprogramm in Qiita-Artikel "Lernen Sie das Entwurfsmuster" Composite "mit Python". Vergleichen Sie daher die Implementierungen. Dies gibt Ihnen ein besseres Verständnis des Besuchermusters.
$ 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
Ähnlicher Code wurde in das Git-Repository hochgeladen. https://github.com/ttsubo/study_of_design_pattern/tree/master/Visitor
--Verzeichnisaufbau
.
├── Main.py
└── visitor
├── __init__.py
├── element.py
└── visitor.py
Die Rolle "Besucher" deklariert eine Besuchsmethode (xxxx) mit der Angabe "besucht xxxx" für jedes bestimmte Element der Datenstruktur (Rolle "ConcreteElement"). visit (xxxx) ist eine Methode zur Verarbeitung von xxxx. Der eigentliche Code wird auf der Seite der Rolle "Konkreter Besucher" geschrieben. Im Beispielprogramm übernimmt die Klasse "Besucher" diese Rolle.
visitor/visitor.py
from abc import ABCMeta, abstractmethod
class Vistor(metaclass=ABCMeta):
@abstractmethod
def visit(self, directory):
pass
Die Rolle "ConcreteVisitor" implementiert die Schnittstelle für die Rolle "Besucher". Implementieren Sie eine Methode des Formularbesuchers (xxxx) und beschreiben Sie die Verarbeitung für jede Rolle "ConcreteElement". Im Beispielprogramm übernimmt die Klasse "ListVistor" diese Rolle.
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
Die Rolle "Element" ist eine Rolle, die das Ziel der Rolle "Besucher" darstellt. Deklarieren Sie eine Akzeptanzmethode, die den Besuch akzeptiert. Die Rolle "Besucher" wird an das Argument der Methode "accept" übergeben. Im Beispielprogramm übernimmt die Klasse "Element" diese Rolle.
visitor/element.py
from abc import ABCMeta, abstractmethod
class Element(metaclass=ABCMeta):
@abstractmethod
def accept(self, v):
pass
Die Rolle "ConcreteElement" ist die Rolle, die die Schnittstelle für die Rolle "Element" implementiert. Im Beispielprogramm übernehmen die Klassen "Eintrag", "Datei" und "Verzeichnis" diese Rolle.
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)
Die Rolle "Objektstruktur" ist eine Rolle, die eine Reihe von "Element" -Rollen verarbeitet. Es verfügt über eine Methode, mit der die Rolle "ConcreteVisitor" einzelne "Element" -Rollen verarbeiten kann. Im Beispielprogramm übernimmt die Klasse "Directory" diese Rolle. (Zwei Rollen pro Person) Die "Directory" -Klasse des Beispielprogramms stellt einen "Iterator" bereit, sodass die "ConcreteVisitor" -Rolle einzelne "Element" -Rollen verarbeiten kann.
Im Beispielprogramm übernimmt die Methode "startMain" diese Rolle.
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()
Fügen Sie eine Ausnahmeklasse hinzu
visitor/element.py
class FileTreatmentException(Exception):
def __init__(self,*args,**kwargs):
self.message = "FileTreatmentException"
Recommended Posts