The Chain of Responsibility Pattern is a squence of handlers processing an event one after another or can abort the chain. Think of a car MOT's you go through the checks and if one fails then you can abort, if not then the chain is complete. The chain can be implemented as a chain of references or a centralized construct (see second example).
| Chain of Responsibility example | # Chain of Responsibility example
class Event:
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
class Widget:
def __init__(self, parent=None):
self.parent = parent
def handle(self, event):
handler = 'handle_{}'.format(event) # the handlers, for example handle_close, handle_default, etc
if hasattr(self, handler):
method = getattr(self, handler)
method(event)
elif self.parent:
self.parent.handle(event)
elif hasattr(self, 'handle_default'):
self.handle_default(event)
class MainWindow(Widget):
def handle_close(self, event):
print('MainWindow: {}'.format(event))
def handle_default(self, event):
print('MainWindow Default: {}'.format(event))
class SendDialog(Widget):
def handle_paint(self, event):
print('SendDialog: {}'.format(event))
class MsgText(Widget):
def handle_down(self, event):
print('MsgText: {}'.format(event))
def main():
mw = MainWindow()
sd = SendDialog(mw)
msg = MsgText(sd)
for e in ('down', 'paint', 'unhandled', 'close'):
evt = Event(e)
print('\nSending event -{}- to MainWindow'.format(evt))
mw.handle(evt)
print('Sending event -{}- to SendDialog'.format(evt))
sd.handle(evt)
print('Sending event -{}- to MsgText'.format(evt))
msg.handle(evt)
if __name__ == '__main__':
main() |
The Command Pattern uses an Object that represents an operation (action/s) that can be rolled back, can also be used for callbacks. You encapsulate all the details of an operation in a separate object (which could be stored to disk) which you can use to rollback any changes. Recording of history and can be used in callbacks, thread queues/pools - decouples thread from the command object action, undoing actions, recording history, GUI buttons, etc.
| Command example | # Command example
import os
verbose = True
class RenameFile:
def __init__(self, path_src, path_dest):
self.src, self.dest = path_src, path_dest
def execute(self):
if verbose:
print("[renaming '{}' to '{}']".format(self.src, self.dest))
os.rename(self.src, self.dest)
def undo(self):
if verbose:
print("[renaming '{}' back to '{}']".format(self.dest, self.src))
os.rename(self.dest, self.src)
class CreateFile:
def __init__(self, path, txt='hello world\n'):
self.path, self.txt = path, txt
def execute(self):
if verbose:
print("[creating file '{}']".format(self.path))
with open(self.path, mode='w', encoding='utf-8') as out_file:
out_file.write(self.txt)
def undo(self):
delete_file(self.path)
class ReadFile:
def __init__(self, path):
self.path = path
def execute(self):
if verbose:
print("[reading file '{}']".format(self.path))
with open(self.path, mode='r', encoding='utf-8') as in_file:
print(in_file.read(), end='')
def delete_file(path):
if verbose:
print("deleting file '{}".format(path))
os.remove(path)
def main():
orig_name, new_name = 'file1', 'file2'
commands = [] # commands list
for cmd in CreateFile(orig_name), ReadFile(orig_name), RenameFile(orig_name, new_name):
commands.append(cmd)
[c.execute() for c in commands] # original order of the commands
answer = input('reverse the executed commands? [y/n] ')
if answer not in 'yY':
print("the result is {}".format(new_name))
exit()
for c in reversed(commands): # reverse the order of the commands
try:
c.undo()
except AttributeError as e:
pass
if __name__ == "__main__":
main() |
The Intepreter Pattern uses textual input (strings) that need to be processed (turned into OOP structures), compilers, HTML, regular expressions, etc are all intepreters. There are two stages firstly separate lexical tokens (lexing) and then interpreting sequences of said tokens (parsing). A good example is the string "3 + 5 * 4 / 2" and turning this into code and calculating the result.
| Intepreter example | # Interpreter example
class RomanNumeralInterpreter(object):
def __init__(self):
self.grammar = {
'I': 1,
'V': 5,
'X': 10,
'L': 50,
'C': 100,
'D': 500,
'M': 1000
}
def interpret(self, text):
numbers = list(map(self.grammar.get, text))
if None in numbers:
raise ValueError('Error value: %s' % text)
result = 0
temp = None
while numbers:
num = numbers.pop(0)
if temp is None or temp >= num:
result += num
else:
result += (num - temp * 2)
temp = num
return result
interp = RomanNumeralInterpreter()
print("Result should equal 3999: " + str(interp.interpret('MMMCMXCIX') == 3999)) # interpret the string
print("Result should equal 1988: " + str(interp.interpret('MCMLXXXVIII') == 1988)) # interpret the string |
The Observer Pattern gets informed when certain things/events happen, it listens to events and notifies when they occur. In observer pattern, the object that watch on the state of another object are called Observer and the object that is being watched is called Subject. Examples of this pattern is magazine publishing, Facebook notifications, Software updates (any thing with publish and subscribe).
| Observer example | # Observer example
class Subject:
def __init__(self):
self.__observers = []
def register(self, observer):
self.__observers.append(observer)
# go through the list of observers calling their own notify method
def notifyAll(self, *args, **kwargs):
for observer in self.__observers:
observer.notify(self, *args, **kwargs)
class Observer1:
def __init__(self, subject):
subject.register(self)
def notify(self, subject, *args):
print(type(self).__name__, ':: Got', args, 'From', subject)
class Observer2:
def __init__(self, subject):
subject.register(self)
def notify(self, subject, *args):
print(type(self).__name__, ':: Got', args, 'From', subject)
def main():
subject = Subject()
observer1 = Observer1(subject) # register the observer1
observer2 = Observer2(subject) # register the observer2
subject.notifyAll('notification')
if __name__ == "__main__":
main() |
| Observer example | # Observer example
from abc import ABCMeta, abstractmethod
class Subscriber(metaclass=ABCMeta):
@abstractmethod
def update(self):
pass
class NewsPublisher:
def __init__(self):
self.__subscribers = []
self.__latestNews = None
def attach(self, subscriber):
self.__subscribers.append(subscriber)
def detach(self):
return self.__subscribers.pop()
def subscribers(self):
return [type(x).__name__ for x in self.__subscribers]
def notifySubscribers(self):
for sub in self.__subscribers:
sub.update()
def addNews(self, news):
self.__latestNews = news
def getNews(self):
return "Got News:", self.__latestNews
class SMSSubscriber:
def __init__(self, publisher):
self.publisher = publisher
self.publisher.attach(self)
def update(self):
print(type(self).__name__, self.publisher.getNews())
class EmailSubscriber:
def __init__(self, publisher):
self.publisher = publisher
self.publisher.attach(self)
def update(self):
print(type(self).__name__, self.publisher.getNews())
class AnyOtherSubscriber:
def __init__(self, publisher):
self.publisher = publisher
self.publisher.attach(self)
def update(self):
print(type(self).__name__, self.publisher.getNews())
if __name__ == '__main__':
news_publisher = NewsPublisher()
# Register the subscribers
for Subscribers in [SMSSubscriber, EmailSubscriber, AnyOtherSubscriber]:
Subscribers(news_publisher)
print("\nSubscribers:", news_publisher.subscribers())
news_publisher.addNews('Hello World!')
news_publisher.notifySubscribers()
print("\nDetached:", type(news_publisher.detach()).__name__)
print("\nSubscribers:", news_publisher.subscribers())
news_publisher.addNews('My second news!')
news_publisher.notifySubscribers() |
The State Pattern changes in state can be explicit or in response to event (Observer Pattern), depending on your state machine you go from one state to another (trigger something to tranform from one state to another state), a formalized construct which manages state and transitions is called state machine. Examples of this are Gumball Machine, Jukebox, Traffic lights, Kettle (on/off), Record Player, etc.
| State example | # State example
from abc import abstractmethod, ABCMeta
class State(metaclass=ABCMeta):
@abstractmethod
def doThis(self):
pass
class StartState(State):
def doThis(self):
print("TV Switching ON..")
class StopState(State):
def doThis(self):
print("TV Switching OFF..")
class TVContext(State):
def __init__(self):
self.state = "OFF" # default state is OFF
def getState(self):
return self.state
def setState(self, state):
self.state = state
def doThis(self):
self.state.doThis()
def main():
context = TVContext()
print("Default State: " + str(context.getState()))
# Start state
start = StartState()
context.setState(start)
context.doThis()
# Stop state
stop = StopState()
context.setState(stop)
context.doThis()
if __name__ == "__main__":
main() |
| State example | # State example
class ComputerState(object):
name = "state"
allowed = []
def switch(self, state):
if state.name in self.allowed:
print('Current:',self,' => switched to new state',state.name)
self.__class__ = state
else:
print('Current:',self,' => switching to',state.name,'not possible.')
def __str__(self):
return self.name
class Off(ComputerState):
name = "off"
allowed = ['on']
class On(ComputerState):
name = "on"
allowed = ['off','suspend','hibernate']
class Suspend(ComputerState):
name = "suspend"
allowed = ['on']
class Hibernate(ComputerState):
name = "hibernate"
allowed = ['on']
class Computer(object):
def __init__(self, model='HP'):
self.model = model
self.state = Off()
def change(self, state):
self.state.switch(state)
if __name__ == "__main__":
comp = Computer()
# Switch on
comp.change(On)
# Switch off
comp.change(Off)
# Switch on again
comp.change(On)
# Suspend
comp.change(Suspend)
# Try to hibernate - cannot!
comp.change(Hibernate)
# switch on back
comp.change(On)
# Finally off
comp.change(Off) |
The Strategy Pattern is used to change a class behavior or its algorithm can be changed at run time (dynamic or static), many algorithms can be decomposed into higher and lower level parts the high level parts can then be reused, examples are a gamer figure could walk, run or swim (we don't know until he/she does it), the output of some text (could be XML, JSON, HTML) again we don't know until the users tells what he/she wants during runtime.
| Strategy example | # Strategy example
class ImageOpener(object):
@staticmethod
def open(filename): # will have to be implemented by all derived classes
raise NotImplementedError()
class PNGImageOpener(ImageOpener):
@staticmethod
def open(filename):
print('PNG: open with Paint')
class JPEGImageOpener(ImageOpener):
@staticmethod
def open(filename):
print('JPG/JPEG: open with ImageViewer')
class SVGImageOpener(ImageOpener):
@staticmethod
def open(filename):
print('SVG: open with Illustrator')
class UnknownImageOpener(ImageOpener):
@staticmethod
def open(filename):
print("You don't have program for %s extension" % filename.split('.')[-1].upper())
class Image(object):
@classmethod
def open_file(cls, filename):
ext = filename.split('.')[-1]
if ext == 'png': # generally in a strategy you have a if/switch/case statement
opener = PNGImageOpener
elif ext in ('jpg', 'jpeg'):
opener = JPEGImageOpener
elif ext == 'svg':
opener = SVGImageOpener
else:
opener = UnknownImageOpener
byterange = opener.open(filename)
return cls(byterange, filename)
def __init__(self, byterange, filename):
self._byterange = byterange
self._filename = filename
def main():
Image.open_file('picture.png')
Image.open_file('picture.jpg')
Image.open_file('picture.svg')
Image.open_file('picture.raw')
if __name__ == "__main__":
main() |
The Template Method Pattern provides high-level blueprints for an algorithm to be completed by inheritors, the template method does the same as the strategy pattern but uses inheritance, the overall algorithm makes use of abstract member, inheritors override the abstract members and parent template method invoked, examples are game structure template, computer base template (add cpu, add memory, etc).
The Template Method Pattern can be confused with the strategy class, Strategy pattern defines a family of algorithms and makes them interchangeable. Client code can use different algorithms since the algorithms are encapsulated. Template method defines the outline of an algorithm and lets subclasses part of the algorithm's implementation. So you can have different implementations of an algorithms steps but retain the algorithm's structure
| Template Method example | # Template Method example
from abc import ABCMeta, abstractmethod
class AbstractClass(metaclass=ABCMeta):
def __init__(self):
pass
@abstractmethod
def operation1(self):
pass
@abstractmethod
def operation2(self):
pass
def template_method(self):
print("Defining the Algorithm. Operation1 follows Operation2")
self.operation2()
self.operation1()
class ConcreteClass(AbstractClass):
def operation1(self):
print("My Concrete Operation1")
def operation2(self):
print("Operation 2 remains same")
class Client:
def main(self):
self.concreate = ConcreteClass()
self.concreate.template_method()
def main():
client = Client()
client.main()
if __name__ == "__main__":
main() |
| Template Method example | # Template Method example
from abc import abstractmethod, ABCMeta
class Trip(metaclass=ABCMeta):
@abstractmethod
def setTransport(self):
pass
@abstractmethod
def day1(self):
pass
@abstractmethod
def day2(self):
pass
@abstractmethod
def day3(self):
pass
@abstractmethod
def returnHome(self):
pass
def itinerary(self):
self.setTransport()
self.day1()
self.day2()
self.day3()
self.returnHome()
class VeniceTrip(Trip):
def setTransport(self):
print("Take a boat and find your way in the Grand Canal")
def day1(self):
print("Visit St Mark's Basilica in St Mark's Square")
def day2(self):
print("Appreciate Doge's Palace")
def day3(self):
print("Enjoy the food near the Rialto Bridge")
def returnHome(self):
print("Get souvenirs for friends and get back")
class MaldivesTrip(Trip):
def setTransport(self):
print("On foot, on any island, Wow!")
def day1(self):
print("Enjoy the marine life of Banana Reef")
def day2(self):
print("Go for the water sports and snorkelling")
def day3(self):
print("Relax on the beach and enjoy the sun")
def returnHome(self):
print("Dont feel like leaving the beach..")
class TravelAgency:
def arrange_trip(self):
choice = input("What kind of place you'd like to go historical or to a beach?")
if choice == 'historical':
self.trip = VeniceTrip()
self.trip.itinerary()
elif choice == 'beach':
self.trip = MaldivesTrip()
self.trip.itinerary()
def main():
TravelAgency().arrange_trip()
if __name__ == "__main__":
main() |