APPENDIX A : Design Pattern Quick Reference
Quick Checks for the Code
- Singleton Pattern
- Prototype Pattern
- Factory Pattern
- Builder Pattern
- Adapter Pattern
- Decorator Pattern
- Facade Pattern
- Proxy Pattern
- Chain of Responsibility Pattern
- Composite
- Command Pattern
- Interpreter Pattern
- Iterator Pattern
- Observer Pattern
- State Pattern
- Strategy Pattern
327
' Wessel Badenhorst 2017
W. Badenhorst, Practical Python Design Patterns, https://doi.org/10.1007/978-1-4842-2680-3
APPENDIX A DESIGN PATTERN QUICK REFERENCE
- Template Method Pattern
- Visitor Pattern
- Model View Controller Pattern
- Publisher Subscriber Pattern
Singleton
singleton.py
class SingletonObject(object): class __SingletonObject():
def __init__(self): self.val = None
def __str__(self):
return "{0!r} {1}".format(self, self.val)
- the rest of the class definition will follow here, as per the previous logging script
instance = None
def __new__(cls):
if not SingletonObject.instance:
SingletonObject.instance = SingletonObject.__SingletonObject()
return SingletonObject.instance
def __getattr__(self, name):
return getattr(self.instance, name)
def __setattr__(self, name):
return setattr(self.instance, name)
328
APPENDIX A DESIGN PATTERN QUICK REFERENCE
Prototype
prototype_1.py
from abc import ABCMeta, abstractmethod
class Prototype(metaclass=ABCMeta): @abstractmethod
def clone(self):
pass
concrete.py
from prototype_1 import Prototype from copy import deepcopy
class Concrete(Prototype): def clone(self):
return deepcopy(self)
Factory
factory.py import abc
class AbstractFactory(object): __metaclass__ = abc.ABCMeta
@abc.abstractmethod def make_object(self):
return
class ConcreteFactory(AbstractFactory): def make_object(self):
- do something
return ConcreteClass()
329
APPENDIX A DESIGN PATTERN QUICK REFERENCE
Builder
form_builder.py
from abc import ABCMeta, abstractmethod class Director(object, metaclass=ABCMeta):
def __init__(self):
self._builder = None
@abstractmethod def construct(self):
pass
def get_constructed_object(self):
return self._builder.constructed_object
class Builder(object, metaclass=ABCMeta):
def __init__(self, constructed_object):
self.constructed_object = constructed_object
class Product(object): def __init__(self):
pass
def __repr__(self):
pass
class ConcreteBuilder(Builder): pass
class ConcreteDirector(Director): pass
330
APPENDIX A DESIGN PATTERN QUICK REFERENCE
Adapter
third_party.py
class WhatIHave(object):
def provided_function_1(self): pass def provided_function_2(self): pass
class WhatIWant(object):
def required_function(): pass adapter.py
from third_party import WhatIHave
class ObjectAdapter(object):
def __init__(self, what_i_have):
self.what_i_have = what_i_have
def required_function(self):
return self.what_i_have.provided_function_1()
def __getattr__(self, attr):
- Everything else is handled by the wrapped object return getattr(self.what_i_have, attr)
Decorator
decorator.py
from functools import wraps def dummy_decorator(f):
@wraps(f)
def wrap_f():
print("Function to be decorated: ", f.__name__) print("Nested wrapping function: ", wrap_f.__name__) return f()
return wrap_f
331
APPENDIX A DESIGN PATTERN QUICK REFERENCE
@dummy_decorator
def do_nothing():
print("Inside do_nothing")
if __name__ == "__main__":
print("Wrapped function: ",do_nothing.__name__) do_nothing()
Facade
simple_ facade.py
class Invoice:
def __init__(self, customer): pass
class Customer:
Altered customer class to try to fetch a customer on init or creates a
new one
def __init__(self, customer_id): pass
class Item:
- Altered item class to try to fetch an item on init or creates a new one
def __init__(self, item_barcode): pass
class Facade:
@staticmethod
def make_invoice(customer): return Invoice(customer)
@staticmethod
def make_customer(customer_id): return Customer(customer_id) @staticmethod
def make_item(item_barcode): return Item(item_barcode)
- ...
332
APPENDIX A DESIGN PATTERN QUICK REFERENCE
Proxy
proxy.py
import time
class RawClass(object):
def func(self, n):
return f(n)
def memoize(fn):
__cache = {}
def memoized(*args):
key = (fn.__name__, args) if key in __cache:
return __cache[key]
__cache[key] = fn(*args) return __cache[key]
return memoized class ClassProxy(object):
def __init__(self, target):
self.target = target
func = getattr(self.target, ’func’) setattr(self.target, ’func’, memoize(func))
def __getattr__(self, name):
return getattr(self.target, name)
333
APPENDIX A DESIGN PATTERN QUICK REFERENCE
Chain of Responsibility
chain_of_responsibility.py
class EndHandler(object): def __init__(self):
pass
def handle_request(self, request):
pass
class Handler1(object):
def __init__(self):
self.next_handler = EndHandler()
def handle_request(self, request):
self.next_handler.handle_request(request)
def main(request):
concrete_handler = Handler1()
concrete_handler.handle_request(request)
if __name__ == "__main__":
- from the command line we can define some request main(request)
Alternative
chain_of_responsibility_alt.py class Dispatcher(object):
def __init__(self, handlers=[]):
self.handlers = handlers
def handle_request(self, request): for handler in self.handlers: request = handler(request)
return request
334
APPENDIX A DESIGN PATTERN QUICK REFERENCE
Command
command.py
class Command:
def __init__(self, receiver, text):
self.receiver = receiver self.text = text
def execute(self):
self.receiver.print_message(self.text)
class Receiver(object):
def print_message(self, text):
print("Message received: {}".format(text))
class Invoker(object):
def __init__(self):
self.commands = []
def add_command(self, command):
self.commands.append(command)
def run(self):
for command in self.commands:
command.execute()
if __name__ == "__main__":
receiver = Receiver()
command1 = Command(receiver, "Execute Command 1") command2 = Command(receiver, "Execute Command 2")
invoker = Invoker() invoker.add_command(command1) invoker.add_command(command2) invoker.run()
335
APPENDIX A DESIGN PATTERN QUICK REFERENCE
Composite
composite.py
class Leaf(object):
def __init__(self, *args, **kwargs):
pass
def component_function(self):
print("Leaf")
class Composite(object):
def __init__(self, *args, **kwargs):
self.children = []
def component_function(self): for child in children:
child.component_function()
def add(self, child):
self.children.append(child)
def remove(self, child):
self.children.remove(child)
Interpreter
interpreter.py
class NonTerminal(object):
def __init__(self, expression):
self.expression = expression
def interpret(self):
self.expression.interpret()
class Terminal(object): def interpret(self):
pass
336
APPENDIX A DESIGN PATTERN QUICK REFERENCE
Iterator
classic_iterator.py
import abc
class Iterator(metaclass=abc.ABCMeta):
@abc.abstractmethod def has_next(self): pass
@abc.abstractmethod
def next(self): pass
class Container(metaclass=abc.ABCMeta):
@abc.abstractmethod def getIterator(self): pass
class MyListIterator(Iterator):
def __init__(self, my_list):
self.index = 0 self.list = my_list.list
def has_next(self):
return self.index < len(self.list)
def next(self):
self.index += 1
return self.list[self.index - 1]
class MyList(Container):
def __init__(self, *args):
self.list = list(args)
def getIterator(self):
return MyListIterator(self)
337
APPENDIX A DESIGN PATTERN QUICK REFERENCE
if __name__ == "__main__":
my_list = MyList(1, 2, 3, 4, 5, 6) my_iterator = my_list.getIterator()
while my_iterator.has_next(): print(my_iterator.next())
Alternative
iterator_alt.py
for element in collection:
do_something(element)
Observer
observer.py
class ConcreteObserver(object):
def update(self, observed):
print("Observing: {}".format(observed))
class Observable(object):
def __init__(self):
self.callbacks = set() self.changed = False
def register(self, callback):
self.callbacks.add(callback)
def unregister(self, callback):
self.callbacks.discard(callback)
def unregister_all(self):
self.callbacks = set()
338
APPENDIX A DESIGN PATTERN QUICK REFERENCE
def poll_for_change(self): if self.changed: self.update_all
def update_all(self):
for callback in self.callbacks:
callback(self)
State
state.py
class State(object):
pass
class ConcreteState1(State):
def __init__(self, state_machine):
self.state_machine = state_machine
def switch_state(self):
self.state_machine.state = self.state_machine.state2
class ConcreteState2(State):
def __init__(self, state_machine):
self.state_machine = state_machine
def switch_state(self):
self.state_machine.state = self.state_machine.state1
class StateMachine(object):
def __init__(self):
self.state1 = ConcreteState1(self) self.state2 = ConcreteState2(self) self.state = self.state1
def switch(self):
self.state.switch_state()
339
APPENDIX A DESIGN PATTERN QUICK REFERENCE
def __str__(self):
return str(self.state)
def main():
state_machine = StateMachine() print(state_machine)
state_machine.switch() print(state_machine)
if __name__ == "__main__":
main()
Strategy
strategy.py
def executor(arg1, arg2, func=None):
if func is None:
return "Strategy not implemented..."
return func(arg1, arg2)
def strategy_1(arg1, arg2): return f_1(arg1, arg2)
def strategy_2(arg1, arg2): return f_2(arg1, arg2)
Template Method
template_method.py
import abc
class TemplateAbstractBaseClass(metaclass=abc.ABCMeta):
def template_method(self):
self._step_1() self._step_2()
self._step_n()
340
APPENDIX A DESIGN PATTERN QUICK REFERENCE
@abc.abstractmethod def _step_1(self): pass
@abc.abstractmethod def _step_2(self): pass
@abc.abstractmethod
def _step_3(self): pass
class ConcreteImplementationClass(TemplateAbstractBaseClass):
def _step_1(self): pass
def _step_2(self): pass
def _step_3(self): pass
Visitor
visitor.py import abc
class Visitable(object):
def accept(self, visitor):
visitor.visit(self)
class CompositeVisitable(Visitable): def __init__(self, iterable): self.iterable = iterable
def accept(self, visitor):
for element in self.iterable: element.accept(visitor)
visitor.visit(self)
341
APPENDIX A DESIGN PATTERN QUICK REFERENCE
class AbstractVisitor(object):
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def visit(self, element):
raise NotImplementedError("A visitor need to define a visit method")
class ConcreteVisitable(Visitable):
def __init__(self):
pass
class ConcreteVisitor(AbstractVisitor):
def visit(self, element):
pass
Model–View–Controller
mvc.py
import sys
class GenericController(object):
def __init__(self):
self.model = GenericModel() self.view = GenericView()
def handle(self, request):
data = self.model.get_data(request) self.view.generate_response(data)
class GenericModel(object):
def __init__(self):
pass
def get_data(self, request):
return {’request’: request}
342
APPENDIX A DESIGN PATTERN QUICK REFERENCE
class GenericView(object): def __init__(self):
pass
def generate_response(self, data):
print(data)
def main(name):
request_handler = GenericController() request_handler.handle(name)
if __name__ == "__main__":
main(sys.argv[1])
Publisher–Subscriber
publish_subscribe.py
class Message(object):
def __init__(self):
self.payload = None self.topic = "all"
class Subscriber(object):
def __init__(self, dispatcher, topic):
dispatcher.subscribe(self, topic)
def process(self, message):
print("Message: {}".format(message.payload))
class Publisher(object):
def __init__(self, dispatcher):
self.dispatcher = dispatcher
def publish(self, message):
self.dispatcher.send(message)
343
APPENDIX A DESIGN PATTERN QUICK REFERENCE
class Dispatcher(object):
def __init__(self):
self.topic_subscribers = dict()
def subscribe(self, subscriber, topic):
self.topic_subscribers.setdefault(topic, set()).add(subscriber)
def unsubscribe(self, subscriber, topic):
self.topic_subscribers.setdefault(topic, set()).discard(subscriber)
def unsubscribe_all(self, topic):
self.subscribers = self.topic_subscribers[topic] = set()
def send(self, message):
for subscriber in self.topic_subscribers[message.topic]:
subscriber.process(message)
Final Words
Well done! You have learned many useful patterns. I hope that you will continue to practice and become a better programmer every day.
Thank you for going on this journey with me. Goodbye and good luck.
344
'Practical Python Design Patterns' 카테고리의 다른 글
PUBLISH–SUBSCRIBE PATTERN (0) | 2023.03.29 |
---|---|
Model-View-Controller Pattern (0) | 2023.03.29 |
Visitor Pattern (0) | 2023.03.29 |
Template Method Pattern (0) | 2023.03.29 |
Strategy Pattern (0) | 2023.03.29 |
댓글