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 Decorator-Muster ist eines der von GoF (Gang of Four; 4 Banden) definierten Designmuster. Mit diesem Muster können Sie vorhandenen Objekten dynamisch neue Funktionen und Verhaltensweisen hinzufügen.
UML class and sequence diagram UML class diagram (Das Obige ist aus Wikipedia zitiert)
Es ist ein Designmuster, das Objekte immer mehr dekoriert. Wenn Sie Sahne auf einen Biskuitkuchen auftragen und Erdbeere darauf legen, ist dies wie ein Erdbeerkuchen. Sie werden häufig angezeigt, wenn Sie an der Python-Programmierung beteiligt sind.
Ich möchte tatsächlich ein Beispielprogramm ausführen, das das Decorator-Muster verwendet, und das folgende Verhalten überprüfen.
HALLO
mit einem Rahmen ein, fügen Sie *
Zeichen davor und danach ein und schließen Sie sie dann zweimal mit einem Rahmen ein ... (im Folgenden weggelassen)$ python Main.py
Hello, world.
#Hello, world.#
+---------------+
|#Hello, world.#|
+---------------+
/+-----------+/
/|+---------+|/
/||*+-----+*||/
/||*|HELLO|*||/
/||*+-----+*||/
/|+---------+|/
/+-----------+/
Es sieht aus wie ein Turm in Hanoi.
Ähnlicher Code wurde in das Git-Repository hochgeladen. https://github.com/ttsubo/study_of_design_pattern/tree/master/Decorator/step1
--Verzeichnisaufbau
.
├── Main.py
└── decorator
├── __init__.py
├── border.py
└── display.py
Es ist ein Kern beim Hinzufügen von Funktionen. Welche Rolle spielt "Komponente"? Definieren Sie nur die Schnittstelle des Biskuitkuchen. Im Beispielprogramm übernimmt die Klasse "Display" diese Rolle.
decorator/display.py
from abc import ABCMeta, abstractmethod
class Display(metaclass=ABCMeta):
@abstractmethod
def getColumns(self):
pass
@abstractmethod
def getRows(self):
pass
@abstractmethod
def getRowText(self, row):
pass
def show(self):
for i in range(self.getRows()):
print(self.getRowText(i))
... (snip)
Es ist ein konkreter Biskuitkuchen, der die Schnittstelle der Rolle der "Komponente" implementiert. Im Beispielprogramm übernimmt die Klasse "StringDisplay" diese Rolle.
decorator/display.py
class StringDisplay(Display):
def __init__(self, string):
self.string = string
def getColumns(self):
return len(self.string)
def getRows(self):
return 1
def getRowText(self, row):
if row == 0:
return self.string
else:
return None
Es hat die gleiche Schnittstelle wie die Rolle "Komponente". Darüber hinaus hat es die Rolle "Komponente", die diese Rolle "Dekorateur" zum Dekorieren hat. Diese Rolle "weiß", was Sie dekorieren. Im Beispielprogramm übernimmt die Klasse "Border" diese Rolle.
decorator/border.py
from decorator.display import Display
class Border(Display):
def __init__(self, display):
self.display = display
Es ist eine spezielle Rolle des "Dekorateurs". Im Beispielprogramm übernehmen die Klassen "SideBorder" und "FullBorder" diese Rolle.
decorator/border.py
class SideBorder(Border):
def __init__(self, display, ch):
super(SideBorder, self).__init__(display)
self.__borderChar = ch
def getColumns(self):
return 1 + self.display.getColumns() + 1
def getRows(self):
return self.display.getRows()
def getRowText(self, row):
return self.__borderChar + self.display.getRowText(row) + self.__borderChar
decorator/border.py
class FullBorder(Border):
def __init__(self, display):
super(FullBorder, self).__init__(display)
def getColumns(self):
return 1 + self.display.getColumns() + 1
def getRows(self):
return 1 + self.display.getRows() + 1
def getRowText(self, row):
if row == 0:
return '+' + self.__make_line('-', self.display.getColumns()) + '+'
elif row == self.display.getRows() + 1:
return '+' + self.__make_line('-', self.display.getColumns()) + '+'
else:
return '|' + self.display.getRowText(row - 1) + '|'
def __make_line(self, char, count):
buf = ''
for _ in range(count):
buf += char
return buf
Im Beispielprogramm übernimmt die Methode "startMain" diese Rolle.
Main.py
from decorator.display import StringDisplay
from decorator.border import SideBorder, FullBorder
def startMain():
b1 = StringDisplay('Hello, world.')
b2 = SideBorder(b1, '#')
b3 = FullBorder(b2)
b1.show()
b2.show()
b3.show()
print("")
b4 = SideBorder(
FullBorder(
FullBorder(
SideBorder(
FullBorder(
StringDisplay('HELLO')
),
'*'
)
)
),
'/'
)
b4.show()
if __name__ == '__main__':
startMain()
In der Python-Programmierung sehe ich oft Dekoratoren, daher möchte ich den startMain
-Methoden-Teil mit ** Python-Dekorator ** umschreiben.
Ähnlicher Code wurde in das Git-Repository hochgeladen. https://github.com/ttsubo/study_of_design_pattern/tree/master/Decorator/step2
decorator/display.py
from abc import ABCMeta, abstractmethod
className = []
class Display(metaclass=ABCMeta):
@abstractmethod
def getColumns(self):
pass
@abstractmethod
def getRows(self):
pass
@abstractmethod
def getRowText(self, row):
pass
def show(self):
for i in range(self.getRows()):
print(self.getRowText(i))
class StringDisplay(Display):
def __init__(self, func):
self.string = "Hello, world."
self.func = func
super(StringDisplay, self).__init__()
def getColumns(self):
return len(self.string)
def getRows(self):
return 1
def getRowText(self, row):
if row == 0:
return self.string
else:
return None
def __call__(self):
global className
className.append(self)
return self.func()
decorator/border.py
from decorator.display import Display, className
class Border(Display):
def __init__(self, display):
self.display = display
super(Border, self).__init__()
class SideBorder(Border):
def __init__(self, func):
self.func = func
self.__borderChar = '#'
super(SideBorder, self).__init__(func)
def getColumns(self):
return 1 + self.display.getColumns() + 1
def getRows(self):
return self.display.getRows()
def getRowText(self, row):
return self.__borderChar + self.display.getRowText(row) + self.__borderChar
def __call__(self):
global className
className.append(self)
return self.func()
class FullBorder(Border):
def __init__(self, func):
self.func = func
super(FullBorder, self).__init__(func)
def getColumns(self):
return 1 + self.display.getColumns() + 1
def getRows(self):
return 1 + self.display.getRows() + 1
def getRowText(self, row):
if row == 0:
return '+' + self.__make_line('-', self.display.getColumns()) + '+'
elif row == self.display.getRows() + 1:
return '+' + self.__make_line('-', self.display.getColumns()) + '+'
else:
return '|' + self.display.getRowText(row - 1) + '|'
def __make_line(self, char, count):
buf = ''
for _ in range(count):
buf += char
return buf
def __call__(self):
global className
className.append(self)
return self.func()
Main.py
from decorator.display import StringDisplay, className
from decorator.border import FullBorder, SideBorder
def startMain():
@FullBorder
@SideBorder
@StringDisplay
def dummy1():
className[2].show()
className[1].show()
className[0].show()
dummy1()
print("")
className.clear()
@SideBorder
@FullBorder
@FullBorder
@SideBorder
@FullBorder
@StringDisplay
def dummy2():
className[0].show()
dummy2()
if __name__ == '__main__':
startMain()
Ich werde es bewegen.
$ python Main.py
Hello, world.
#Hello, world.#
+---------------+
|#Hello, world.#|
+---------------+
#+-------------------+#
#|+-----------------+|#
#||#+-------------+#||#
#||#|Hello, world.|#||#
#||#+-------------+#||#
#|+-----------------+|#
#+-------------------+#
Bei dieser Methode ** wird das Rahmenzeichen mit "#" und die Zeichenfolge "Hallo Welt" eingebettet, was zu einem Verlust der Programmierflexibilität führt. .. ** ** **
Lassen Sie uns die Verwendung von Python-Dekoratoren weiter verbessern, damit Sie Rahmenzeichen und Zeichenfolgen rechtzeitig angeben können. Ich verweise auf diesen [Qiita-Artikel "Verständnis der Funktionsweise von WSGI-Anwendungen mit OpenStack" (https://qiita.com/ttsubo/items/0a1abecce272559edc94).
Ähnlicher Code wurde in das Git-Repository hochgeladen. https://github.com/ttsubo/study_of_design_pattern/tree/master/Decorator/step3
decorator/display.py
from abc import ABCMeta, abstractmethod
className = []
class Display(metaclass=ABCMeta):
@abstractmethod
def getColumns(self):
pass
@abstractmethod
def getRows(self):
pass
@abstractmethod
def getRowText(self, row):
pass
def show(self):
for i in range(self.getRows()):
print(self.getRowText(i))
class StringDisplay(Display):
def __init__(self, func, string):
self.string = string
self.func = func
super(StringDisplay, self).__init__()
def getColumns(self):
return len(self.string)
def getRows(self):
return 1
def getRowText(self, row):
if row == 0:
return self.string
else:
return None
def __call__(self):
global className
className.append(self)
return self.func()
def StringDisplay_filter(string):
def filter(func):
return StringDisplay(func, string)
return filter
decorator/border.py
from decorator.display import Display, className
class Border(Display):
def __init__(self, display):
self.display = display
super(Border, self).__init__()
class SideBorder(Border):
def __init__(self, func, ch):
self.func = func
self.__borderChar = ch
super(SideBorder, self).__init__(func)
def getColumns(self):
return 1 + self.display.getColumns() + 1
def getRows(self):
return self.display.getRows()
def getRowText(self, row):
return self.__borderChar + self.display.getRowText(row) + self.__borderChar
def __call__(self):
global className
className.append(self)
return self.func()
def SideBorder_filter(ch):
def filter(func):
return SideBorder(func, ch)
return filter
class FullBorder(Border):
def __init__(self, func):
self.func = func
super(FullBorder, self).__init__(func)
def getColumns(self):
return 1 + self.display.getColumns() + 1
def getRows(self):
return 1 + self.display.getRows() + 1
def getRowText(self, row):
if row == 0:
return '+' + self.__make_line('-', self.display.getColumns()) + '+'
elif row == self.display.getRows() + 1:
return '+' + self.__make_line('-', self.display.getColumns()) + '+'
else:
return '|' + self.display.getRowText(row - 1) + '|'
def __make_line(self, char, count):
buf = ''
for _ in range(count):
buf += char
return buf
def __call__(self):
global className
className.append(self)
return self.func()
Main.py
from decorator.display import StringDisplay_filter, className
from decorator.border import SideBorder_filter, FullBorder
def startMain():
@FullBorder
@SideBorder_filter('#')
@StringDisplay_filter("Hello, world.")
def dummy1():
className[2].show()
className[1].show()
className[0].show()
dummy1()
print("")
className.clear()
@SideBorder_filter('/')
@FullBorder
@FullBorder
@SideBorder_filter('*')
@FullBorder
@StringDisplay_filter("HELLO")
def dummy2():
className[0].show()
dummy2()
if __name__ == '__main__':
startMain()
Ich werde es bewegen.
$ python Main.py
Hello, world.
#Hello, world.#
+---------------+
|#Hello, world.#|
+---------------+
/+-----------+/
/|+---------+|/
/||*+-----+*||/
/||*|HELLO|*||/
/||*+-----+*||/
/|+---------+|/
/+-----------+/
Da es sich um das erste Operationsergebnis handelt, ist es vorerst abgeschlossen.
Recommended Posts