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() |