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