Modèles Gang of Four (GoF) en 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

Modèles Gang of Four (GoF) en Python
Modèles comportementaux en Python
Modèles structurels en Python
Modèles de création en Python
Modèles de conception en Python: introduction
Implémentation du tri rapide en Python
[Gang of Four] Apprentissage des modèles de conception
Manipulation des pixels d'image en Python
Diviser timedelta dans la série Python 2.7
Échappement automatique des paramètres MySQL en python
Gestion des fichiers JSON en Python
Implémentation du jeu de vie en Python
Affichage de la forme d'onde audio en Python
La loi des nombres en python
Implémentation du tri original en Python
Brouillage réversible d'entiers en Python
Conversion de la chaîne <-> date (date, datetime) en Python
[Gang of Four] Apprentissage des modèles de conception --Singleton
Vérifiez le comportement du destroyer en Python
[Gang of Four] Apprentissage des modèles de conception - Décorateur
Pratique d'utilisation de ceci en Python (mauvais)
[Gang of Four] Apprentissage des modèles de conception - Médiateur
Arborescence de sortie des fichiers en Python
Afficher une liste d'alphabets en Python 3
Comparaison des modules de conversion japonais en Python3
[Gang of Four] Apprentissage des modèles de conception - Itérateur
Résumé de diverses instructions for en Python
Le résultat de l'installation de python sur Anaconda
[Gang of Four] Apprentissage des modèles de conception - Façade
[Gang of Four] Apprentissage des modèles de conception - Composite
[Gang of Four] Apprentissage des modèles de conception - Prototype
Principes de base pour exécuter NoxPlayer en Python
Remplacement en bloc des chaînes dans les tableaux Python
Projet Euler # 16 "Somme des pouvoirs" en Python
[Gang of Four] Apprentissage des modèles de conception --Mémento
Traffic Safety-kun: Reconnaissance des panneaux de signalisation en Python
[Gang of Four] Apprentissage des modèles de conception - État
[Gang of Four] Apprentissage des modèles de conception - Interprétation
[Gang of Four] Apprentissage des modèles de conception - Constructeur
[Gang of Four] Apprentissage des modèles de conception - Pont
Résumé des méthodes intégrées, etc. de la liste Python
Utilisation d'opérateurs non logiques de ou en python
À la recherche du FizzBuzz le plus rapide en Python
Exemple pratique d'architecture hexagonale en Python
[Gang of Four] Apprentissage des modèles de conception - Proxy
Projet Euler # 17 "Nombre de caractères" en Python
Equation de mouvement à double pendule en python
[Gang of Four] Apprentissage des modèles de conception - Stratégie
[Gang of Four] Apprentissage des modèles de conception - Adaptateur
Débarrassez-vous des images DICOM en Python
[Gang of Four] Apprentissage des modèles de conception --Observer
Statut de chaque système de traitement Python en 2020
Projet Euler # 1 "Multiple de 3 et 5" en Python
[Gang of Four] Apprentissage des modèles de conception - Commande
Quadtree en Python --2
Python en optimisation
CURL en Python
Sortie du nombre de cœurs de processeur en Python
Dessiner un graphique d'une fonction quadratique en Python
Géocodage en python