Gang of Four (GoF) Patterns 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) Patterns in Python
Behavioral Patterns in Python
Structural Patterns in Python
Creational Patterns in Python
Design Patterns in Python: Introduction
Implementation of quicksort in Python
[Gang of Four] Design pattern learning
Pixel manipulation of images in Python
Division of timedelta in Python 2.7 series
MySQL-automatic escape of parameters in python
Handling of JSON files in Python
Implementation of life game in Python
Waveform display of audio in Python
Law of large numbers in python
Implementation of original sorting in Python
Reversible scrambling of integers in Python
Conversion of string <-> date (date, datetime) in Python
[Gang of Four] Design pattern learning --Singleton
Check the behavior of destructor in Python
[Gang of Four] Design Pattern Learning --Decorator
(Bad) practice of using this in Python
[Gang of Four] Design pattern learning --Mediator
Output tree structure of files in Python
Display a list of alphabets in Python 3
Comparison of Japanese conversion module in Python3
[Gang of Four] Design pattern learning --Iterator
Summary of various for statements in Python
The result of installing python in Anaconda
[Gang of Four] Design pattern learning --Facade
[Gang of Four] Design pattern learning --Composite
[Gang of Four] Design pattern learning --Prototype
The basics of running NoxPlayer in Python
Bulk replacement of strings in Python arrays
Project Euler # 16 "Sum of Powers" in Python
[Gang of Four] Design pattern learning --Memento
Traffic Safety-kun: Recognition of traffic signs in Python
[Gang of Four] Design pattern learning --State
[Gang of Four] Design pattern learning --Interpreter
[Gang of Four] Design pattern learning --Builder
[Gang of Four] Design pattern learning --Bridge
Summary of built-in methods in Python list
Non-logical operator usage of or in python
In search of the fastest FizzBuzz in Python
Practical example of Hexagonal Architecture in Python
[Gang of Four] Design pattern learning --Proxy
Project Euler # 17 "Number of Characters" in Python
Double pendulum equation of motion in python
[Gang of Four] Design pattern learning --Strategy
[Gang of Four] Design pattern learning --Adapter
Get rid of DICOM images in Python
[Gang of Four] Design pattern learning --Observer
Status of each Python processing system in 2020
Project Euler # 1 "Multiples of 3 and 5" in Python
[Gang of Four] Design pattern learning --Command
Meaning of using DI framework in Python
Quadtree in Python --2
Python in optimization
CURL in python
Output the number of CPU cores in Python
Draw a graph of a quadratic function in Python
Geocoding in python