Skip to content

SebdeWell/python_design_patterns

 
 

Repository files navigation

Python Design Patterns

Description

  • This repository contains an overview of the Gang of Four (GoF) design patterns with modern-day variations, adjustments, and discussions in Python.

Learning Objectives

  • SOLID Design Principles:
  • Creational Design Patterns:
    • Builder
      • A builder is a separate component for building an object
      • Can either give builder an initializer or return it via a static function
      • To make builder fluent, return self
      • Different facets of an object can be built with different builders working in tandem via a base class
    • Factories (Factory Method and Abstract Factory)
      • A factory method is a static method that creates object
      • A factory is an entity that can take care of object creation
      • A factory can be external or reside inside the object as an inner class
      • Hierarchies of factories can be used to create related objects
    • Prototype
      • To implement a prototype, partially construct an object and store it somewhere
      • Deep copy the prototype
      • Customize the resulting instance
      • A factory provides a convenient API for using prototypes
    • Singleton
      • Different realizations of Singleton: custom allocator, decorator, metaclass
      • Laziness is easy, just init on first request
      • Monostate variation
      • Testability issues
  • Structural Design Patterns:
    • Adapter
      • Implementing an Adapter is easy
      • Determine the API you have and the API you need
      • Create a component which aggregates (has a reference to, ...) the adaptee
      • Intermediate representations can pile up: use caching and other optimizations
    • Bridge
      • Decouple abstraction from implementation
      • Both can exist as hierarchies
      • A stronger form of encapsulation
    • Composite
      • Objects can use other objects via inheritance/composition
      • Some composed and singular objects need similar/identical behaviors
      • Composite design pattern lets us treat both types of objects uniformly
      • Python supports iteration with __iter__ and Iterable ABC
      • A single object can itself iterable by yielding self from __iter__
    • Decorator
      • A decorator keeps the reference to the decorated object(s)
      • Adds utility attributes and methods to augment the object's features
      • May or may not forward calls to the underlying object
      • Proxying of underlying calls can be done dynamically
      • Python's functional decorators wrap functions; no direct relation to the GoF Decorator Pattern
    • Façade
      • Build a Facade to provide a simplified API over a set of classes
      • May wish to (optionally) expose internals through the facade
      • May allow users to 'escalate' to use more complex API
    • Flyweight
      • Store common data externally
      • Specify an index or a reference into the external data store
      • Define the idea of 'ranges' on homogeneuous collections and store data related to those ranges
    • Proxy
      • A proxy has the same interface as underlying object
      • To create a proxy, simple replicate the existing interface of an object
      • Add relevant functionality to the redefined member functions
      • Different proxies (communications, logging, caching, etc.) have completely different behaviors
  • Behavioral Design Patterns
    • Chain of Responsibility
      • Chain of responsibility can be implemented as a chain of references or a centralized construct
      • Enlist objects in the chain, possibly controlling their order
      • Object removal from chain (e.g., __exit__)
    • Command
      • Encapsulate all details of an operation in a separate object
      • Define instruction for applything the command (either in the command itself, or elsewhere)
      • Optionally define instructions for undoing the command
      • Can create composite commands (a.k.a. macros)
    • Interpreter
      • Barring simple cases, an interpreter acts in two stages
      • Lexing turns text into a set of tokens
      • Parsing tokens into meaningful construct
    • Iterator
      • An iterator specified how you can traverse an object
      • Stateful iterators cannot be recursive
      • yield allows for much more succinct iteration
    • Mediator
      • Create the mediator and have each object in the system refer to it
        • E.g., in a property
      • Mediator engages in bidirectional communication with its connected components
      • Mediator has functions the components can call
      • Components have functions the mediator can call
      • Event processing (e.g., Rx) libraries make communication easier to implement
    • Memento
      • Mementos are used to roll back states arbitrarily
      • A memento is simply a token/handle class with typically no function of its own
      • A memento is not required to expose directly the state(s) to which it reverts the system
      • Can be used to implement undo/redo
    • Observer
      • Observer is an intrusive approach: an observable must provide an event to subscribe to
      • Subsciption and unsubscription with additional/removal of items in list
      • Property notifications are easy: dependent property notifications are tricky
    • State
      • Given sufficient complexity, it pays to formally define possible states and events/triggers
      • Can define
        • State entry/exit behaviors
        • Action when a particular event causes a transition
        • Guard conditions enabling/disabling a transition
        • Default action when no transitions are found for an event
    • Strategy
      • Define an algorithm at a high level
      • Define the interface you expect each strategy to follow
      • Provide for dynamic composition of strategies in the resulting object
    • Template
      • Define an algorithm at a high level in parent class
      • Define constituent parts as abstract methods/properties
      • Inherit the algorthm class, providing the necessary ovverides
    • Visitor
      • OOP double-dispatch approach is not necessary in Python
      • Make a visitor, decorating each 'overload' with @visitor
      • Call visit() and the entire structure gets traversed

Usage

  • All files are created and executed on Mac OS X 10.11 with Python3 (version 3.7)

Awknowledgements

  • Dmitri Nesteruk's course "Design Patterns in Python" on Udemy.com

Author

About

Gang of Four design patterns in Python

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Python 100.0%