[GO] Lernen Sie das Designmuster "Decorator" mit Python

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.

■ Dekorateur (Dekorationsmuster)

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 W3sDesign_Decorator_Design_Pattern_UML.jpg UML class diagram decorator.png (Das Obige ist aus Wikipedia zitiert)

□ Memorandum

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.

■ Beispielprogramm "Decorator"

Ich möchte tatsächlich ein Beispielprogramm ausführen, das das Decorator-Muster verwendet, und das folgende Verhalten überprüfen.

$ python Main.py 
Hello, world.
#Hello, world.#
+---------------+
|#Hello, world.#|
+---------------+

/+-----------+/
/|+---------+|/
/||*+-----+*||/
/||*|HELLO|*||/
/||*+-----+*||/
/|+---------+|/
/+-----------+/

Es sieht aus wie ein Turm in Hanoi.

■ Details zum Beispielprogramm

Ä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

(1) Rolle der Komponente

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)

(2) Rolle der Betonkomponente

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

(3) Die Rolle des Dekorateurs

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

(4) Die Rolle des Betondekorateurs

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

(5) Die Rolle des Kunden

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()

□ Memorandum (Versuchen Sie es mit Python Decorator!) -Teil 1-

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. .. ** ** **

□ Memorandum (Versuchen Sie es mit Python Decorator!) -Teil 2-

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.

■ Referenz-URL

Recommended Posts

Lernen Sie das Designmuster "Decorator" mit Python
Lernen Sie das Entwurfsmuster "Prototype" mit Python
Lernen Sie das Entwurfsmuster "Builder" mit Python
Lernen Sie das Designmuster "Flyweight" in Python
Lernen Sie das Entwurfsmuster "Observer" in Python
Lernen Sie das Entwurfsmuster "Memento" mit Python
Lernen Sie das Entwurfsmuster "Proxy" in Python
Lernen Sie das Entwurfsmuster "Befehl" in Python
Lernen Sie das Entwurfsmuster "Bridge" mit Python
Lernen Sie das Entwurfsmuster "Mediator" mit Python
Lernen Sie das Entwurfsmuster "Iterator" mit Python
Lernen Sie das Entwurfsmuster "Strategie" mit Python
Lernen Sie das Entwurfsmuster "Composite" mit Python
Lernen Sie das Entwurfsmuster "State" in Python
Lernen Sie das Entwurfsmuster "Adapter" mit Python
Lernen Sie das Entwurfsmuster "Abstract Factory" mit Python
Lernen Sie das Entwurfsmuster "Vorlagenmethode" in Python
Lernen Sie das Entwurfsmuster "Chain of Responsibility" in Python
Lernen Sie das Entwurfsmuster "Singleton" mit Python
Lernen Sie das Designmuster "Facade" mit Python
Implementieren Sie das Singleton-Muster in Python
Singleton-Muster in Python
Besuchermuster in Python
Dekorationsmuster in Java
Finde Fehler in Python
Entwurfsmuster in Python: Einführung
Python Design Pattern - Template-Methode
Python im Browser: Brythons Empfehlung
Speichern Sie die Binärdatei in Python
Klicken Sie in Python auf die Sesami-API
Holen Sie sich den Desktop-Pfad in Python
[Python] Was ist @? (Über Dekorateure)
Holen Sie sich den Skriptpfad in Python
Im Python-Befehl zeigt Python auf Python3.8
Klicken Sie auf die Web-API in Python
Ich habe die Warteschlange in Python geschrieben
Berechnen Sie den Vormonat in Python
Untersuchen Sie die Klasse eines Objekts mit Python
Holen Sie sich den Desktop-Pfad in Python
Holen Sie sich den Hostnamen in Python
Greifen Sie mit Python auf die Twitter-API zu
Der erste Schritt von Python Matplotlib
Ich habe den Stack in Python geschrieben
Beherrsche das schwache Ref-Modul in Python
Lernen Sie die Grundlagen von Python ① Grundlegende Anfänger
Versuchen Sie es mit der Wunderlist-API in Python
Überprüfen Sie das Verhalten des Zerstörers in Python
[Python Kivy] Über das Ändern des Designthemas
Versuchen Sie, die Kraken-API mit Python zu verwenden
Lernen Sie die Grundlagen, während Sie Python-Variablen berühren
Schreiben Sie den Test in die Python-Dokumentzeichenfolge
Nehmen Sie die logische Summe von List in Python (Zip-Funktion)
GoF-Entwurfsmuster aus dem Problem 2. Struktur
Zeigen Sie Python 3 im Browser mit MAMP an
Tweet mit der Twitter-API in Python
Dekorateur zur Vermeidung von UnicodeEncodeError in Python 3 print ()
Überprüfen Sie, ob die URL in Python vorhanden ist
Ordnen Sie die in pythons models.py festgelegte Tabelle zu