Gang of Four (GoF) -Muster in Python

Facade: Facade is a structural pattern and consists of a Facade class and a set of subsystems it represents through its own simple and unified interface. This way, the users of the Facade interface don't have to know all the details of the subsystem interfaces to use their functionality.

This is because Facade can hide the subsystem modifications behind itself and, therefore, keep it's own interface as stable as possible. This promotes loose coupling between client and subsystem classes, which in turn, increases the maintainability of the system as a whole. Using the Facade pattern does not prevent the option of accessing subsystem classes directly. Programmers still keep the flexibility of having full visibility to more complex, but nuanced, subsystem interfaces.

class SubsystemA:

	def method1(self):
		print('SubsystemA method1 ...')
		
	def method2(self):
		print('SubsystemA method2 ...')

class SubsystemB:
	
	def method1(self):
		print('SubsystemB method1 ...')
		
	def method2(self):
		print('SubsystemB method2 ...')

class Facade:

	def __init__(self):
		self._subsystem_A = SubsystemA()
		self._subsystem_B = SubsystemB()

	def method(self):
		self._subsystem_A.method1()
		self._subsystem_A.method2()
		
		self._subsystem_B.method1()
		self._subsystem_B.method2()

def main():
	facade = Facade()
	facade.method()

if __name__ == "__main__":
	main()

Command The structure of the command pattern is straight forward. It features a parent class called command, which provides an interface that allows you to execute a method. Users of the command pattern write their own concrete commands, which inherit from the command class. One of the reasons you need to encapsulate a method is to delay or queue the execution of an action on a receiver object. This capability is especially useful when you need to dynamically compose a sequence of behavior or undo actions. By the way, the word encapsulate here is a fancy way of saying wrapping. To reiterate, the concrete commands are our mechanism for invoking an action on a receiver object.

class Command:
	def execute(self):
		pass

class Copy(Command):
	def execute(self):
		print("Copying ...")

class Paste(Command):
	def execute(self):
		print("Pasting ...")

class Save(Command):
	def execute(self):
		print("Saving ...")

class Macro:
	def __init__(self):
		self.commands = []

	def add(self, command):
		self.commands.append(command)

	def run(self):
		for o in self.commands:
			o.execute()

def main():
	macro = Macro()
	macro.add(Copy())
	macro.add(Paste())
	macro.add(Save())
	macro.run()

if __name__ == "__main__":
	main()

Interpreter The pattern is more about a small, custom language you need to create to avoid writing different code repeatedly to conduct similar tasks. A good example is a problem of searching particular patterns in a text. We already have a well-developed language for conducting these specialized tasks. It's called regular expression. The interpreter pattern consists of a parent class called abstract expression and two types of its child classes that are non-terminal expression and terminal expression.

from abc import ABC, abstractmethod

class AbstractExpression():

	@abstractmethod
	def interpret(self):
		pass

class NonterminalExpression(AbstractExpression):

	def __init__(self, expression):
		self._expression = expression

	def interpret(self):
		print("Non-terminal expression being interpreted ...")
		self._expression.interpret()

class TerminalExpression(AbstractExpression):

	def interpret(self):
		print("Terminal expression being interpreted ...")

def main():

	ast = NonterminalExpression(NonterminalExpression(TerminalExpression()))
	ast.interpret()

if __name__ == "__main__":
	main()

UML Python is a great language for programming computers; however, it is not appropriate for describing a design concept. UML is a visual language and consists of many diagrams.

Mediator When there are too many objects interacting with each other, individually, the complexity of the code can increase dramatically, and its maintainability also decreases. In particular, a simple change in one object can have a huge ripple effect on the rest of the code. We call this phenomenon tight coupling among objects. A logical solution to this problem is creating and designating an object as a mediator for other objects. The mediator pattern is the design pattern community's answer to implementing this solution. As its name suggests, a mediator is a behavioral pattern. The pattern consists of a mediator class and its colleague classes. The colleague classes do not communicate with each other directly and therefore not coupled strongly. Note that the respective relationship among the colleague objects are embodied in the mediator class. This eliminates otherwise complex communications that could have resulted from the many to many message exchanges among the colleague objects. In summary, when you need to promote loose coupling between your objects and increase their maintainability, the mediator pattern is a perfect design solution for you.

import sys

class Colleague(object):
	def __init__(self, mediator, id):
		self._mediator = mediator
		self._id = id

	def getID(self):
		return self._id

	def send(self, msg):
		pass

	def receive(self, msg):
		pass

class ConcreteColleague(Colleague):
	def __init__(self, mediator, id):
		super().__init__(mediator, id)

	def send(self, msg):
		print("Message '" + msg + "' sent by Colleague " + str(self._id))
		self._mediator.distribute(self, msg)

	def receive(self, msg):
		print("Message '" + msg + "' received by Colleague " + str(self._id))


class Mediator:
	def add(self, colleague):
		pass

	def distribute(self, sender, msg):
		pass

class ConcreteMediator(Mediator):
	def __init__(self):
		Mediator.__init__(self)
		self._colleague = []

	def add(self, colleague):
		self._colleague.append(colleague)

	def distribute(self, sender, msg):
		for colleague in self._colleague:
			if colleague.getID() != sender.getID():
				colleague.receive(msg)


def main():
	mediator = ConcreteMediator()

	c1 = ConcreteColleague(mediator, 1)
	c2 = ConcreteColleague(mediator, 2)
	c3 = ConcreteColleague(mediator, 3)

	mediator.add(c1)
	mediator.add(c2)
	mediator.add(c3)

	c1.send("Good Morning!")

if __name__ == "__main__":
	main()

Memento This pattern allows you to capture the internal state of an object at a certain point in time, and to restore that same state later in the future if necessary. An object can take on this task in addition to its core mission, but this will make it bulkier and more complex. A better solution is to introduce a new object dedicated to storing the history of transactions associated with the other object. Note that the goal here is not to store every single property of a target object, but to get only those relevant to your undo operation. The memento pattern uses three classes to accomplish its intent. The memento class is responsible for storing the internal state information of an object, in addition the memento class has interfaces available to the originator class. Originator creates a memento object to capture the internal state of an object. It also uses the same memento object to restore the previous state. Caretaker maintains the memento objects, and does not have direct access to its interfaces. Memento organizes your code efficiently by separating the programming tasks of maintaining object states from capturing and restoring operations.

import pickle

class Originator:

	def __init__(self):
		self._state = None

	def create_memento(self):
		return pickle.dumps(vars(self))

	def set_memento(self, memento):
		previous_state = pickle.loads(memento)
		vars(self).clear
		vars(self).update(previous_state)

def main():
	originator = Originator()

	print(vars(originator))

	memento = originator.create_memento()
	
	originator._state = True

	print(vars(originator))

	originator.set_memento(memento)

	print(vars(originator))

if __name__ == "__main__":
	main()

State The state design pattern offers a behavioral design solution to accommodating this type of programming needs. The pattern has the state class, which is extended by its child classes. It's representing a different state in which an object can find itself. The context class is what a client interacts with to set state and its corresponding behavior. If I were asked to program a particular superhero, my choice of design pattern would of course be the state parent because the character transforms.

class AtmState():

	name = "state"
	allowed = []

	def goNext(self, state):
		if state.name in self.allowed:
			print("Current State: ", self, " switched to: ", state.name)
			self.__class__ = state

		else:
			print("Current State: ", self, " switching to: ", state.name, " not possible!")

	def __str__(self):
		return self.name

class Off(AtmState):

	name = "off"
	allowed = ['on']

class On(AtmState):

	name = "on"
	allowed = ['off']

class ATM():
	
	def __init__(self):
		self.current = Off()

	def setState(self, state):
		self.current.goNext(state)

def main():
	atm = ATM()

	atm.setState(On)
	atm.setState(Off)
	atm.setState(Off)

if __name__ == "__main__":
	main()

Template Method By definition, in object oriented programming, inheritance allows a child class to override a method defined by it's parent class. This capability is referred to as polymorphism. It allows or forces only a select group of methods to be overridden and prevents the rest of methods belonging to a parent class from being changed by it's child classes. By restricting how much it's child class behaviors can deviate from it's parent class, the template method pattern defines a skeleton of operations that will always be present in child classes. It is a solution that allows developers to extract commonalities across similar classes and to place them into a class whose template methods offer common interfaces. By doing this, developers can avoid an undesirable situation in which they have to make widespread changes among similar software components that do not implement the template method pattern. The structure of the template method pattern is pretty straight forward. It defines a base class with a set of methods among which some placeholder methods exist whose sole purpose is to enable it's child classes to define them on their own. Frameworks adopt template methods for the obvious purpose of keeping the control over what can be customized by it's users and what needs to stay the same across the board. The framework calls the shots instead of it's adopters.

import sys

from abc import ABC, abstractmethod

class AbstractClass(ABC):
#This class inherit from Abstract Base Class to allow the use of the @abstractmethod decorator
    
	def template_method(self):
		"""Ths is the template method that contains a collection of 
		methods to stay the same, to be overriden, and to be overriden optionally.
		"""

		self.__always_do_this()
		self.do_step_1()
		self.do_step_2()
		self.do_this_or()

	def __always_do_this(self):
		#This is a protected method that should not be overriden.

		name = sys._getframe().f_code.co_name
		print('{}.{}'.format(self.__class__.__name__, name))

	@abstractmethod
	def do_step_1(self):
		#This method should be overriden
		pass

	@abstractmethod
	def do_step_2(self):
		#This method should be overriden
		pass

	def do_this_or(self):
		print('You can overide me but you do not have to')

class ConcreteClassA(AbstractClass):
#This class inherits from the Abstract class featuring the template method. 

	def do_step_1(self):
		print('Doing step 1 for ConcreteClassA ...')

	def do_step_2(self):
		print('Doing step 2 for ConcreteClassA ...')

class ConcreteClassB(AbstractClass):
#This class inherits from the Abstract class featuring the template method.

	def do_step_1(self):
		print('Doing step 1 for ConcreteClassB ...')

	def do_step_2(self):
		print('Doing step 2 for ConcreteClassB ...')

	def do_this_or(self):
		print('Doing my own business ...')

def main():
	print('==ConcreteClassA==')
	a = ConcreteClassA()
	a.template_method()

	print('==ConcreteClassB==')
	b = ConcreteClassB()
	b.template_method()

if __name__ == '__main__':
	main()

More Design Patterns in Python

More information: Python Design Patterns

Recommended Posts

Gang of Four (GoF) -Muster in Python
Verhaltensmuster in Python
Strukturmuster in Python
Erstellungsmuster in Python
Entwurfsmuster in Python: Einführung
Implementierung der schnellen Sortierung in Python
[Viererbande] Designmuster lernen
Bildpixel-Manipulation in Python
Zeitdelta in Python 2.7-Serie teilen
MySQL-automatische Escape-Funktion von Parametern in Python
Umgang mit JSON-Dateien in Python
Implementierung eines Lebensspiels in Python
Audio-Wellenform-Anzeige in Python
Das Gesetz der Zahlen in Python
Implementierung der ursprünglichen Sortierung in Python
Reversibles Verwürfeln von Ganzzahlen in Python
Konvertierung der Zeichenfolge <-> Datum (Datum, Datum / Uhrzeit) in Python
[Viererbande] Design Pattern Learning - Singleton
Überprüfen Sie das Verhalten des Zerstörers in Python
[Viererbande] Design Pattern Learning --Decorator
Übung, dies in Python zu verwenden (schlecht)
[Viererbande] Design Pattern Learning - Vermittler
Ausgabebaumstruktur von Dateien in Python
Zeigen Sie eine Liste der Alphabete in Python 3 an
Vergleich japanischer Konvertierungsmodule in Python3
[Viererbande] Designmusterlernen --Iterator
Zusammenfassung verschiedener for-Anweisungen in Python
Das Ergebnis der Installation von Python auf Anaconda
[Viererbande] Designmuster lernen - Fassade
[Viererbande] Design Pattern Learning - Composite
[Viererbande] Designmuster lernen - Prototyp
Grundlagen zum Ausführen von NoxPlayer in Python
Massenersatz von Zeichenfolgen in Python-Arrays
Projekt Euler # 16 "Summe der Kräfte" in Python
[Viererbande] Designmuster lernen - Andenken
Traffic Safety-Kun: Erkennung von Verkehrszeichen in Python
[Viererbande] Designmuster lernen - Staat
[Vierergruppe] Design Pattern Learning - Interpreter
[Viererbande] Design Pattern Learning - Builder
[Viererbande] Designmuster lernen - Brücke
Zusammenfassung der integrierten Methoden usw. der Python-Liste
Nicht logische Operatorverwendung von oder in Python
Auf der Suche nach dem schnellsten FizzBuzz in Python
Praktisches Beispiel für hexagonale Architektur in Python
[Viererbande] Designmuster lernen --Proxy
Projekt Euler # 17 "Anzahl der Zeichen" in Python
Doppelte Pendelbewegungsgleichung in Python
[Viererbande] Design Pattern Learning - Strategie
[Viererbande] Designmuster lernen --Adapter
Entfernen Sie DICOM-Bilder in Python
[Viererbande] Design Pattern Learning - Beobachter
Status jedes Python-Verarbeitungssystems im Jahr 2020
Projekt Euler # 1 "Vielfaches von 3 und 5" in Python
[Viererbande] Designmuster lernen - Befehl
Quadtree in Python --2
Python in der Optimierung
CURL in Python
Geben Sie die Anzahl der CPU-Kerne in Python aus
Zeichnen Sie in Python ein Diagramm einer quadratischen Funktion
Geokodierung in Python