TUTORIAL SERIES

Design Patterns in Python: A Series

Mastering Design Patterns

Amir Lavasani
7 min readSep 3, 2023

This article marks the beginning of an exciting series where I delve into the intricacies of various design patterns in Python.

Why am I Writing yet Another Tutorial?

The reasons behind launching this series are multi-fold. Firstly, design patterns possess a timeless significance, and I’m embarking on a personal journey to revisit and review each pattern. I’m excited to share this enriching experience with Medium readers.

Secondly, the design pattern content landscape comprises two extremes: the original comprehensive book-style tutorials, replete with examples and intricate scenarios, and the succinct versions that merely provide diagrams and sample code. This series strives for a middle ground, offering comprehensive essentials while maintaining a concise format suited for our fast-paced era.

Lastly, to offer a unique perspective by exclusively focusing on design patterns through the lens of the Python language. I’ve chosen Python for several reasons, including its robust object-oriented capabilities, user-friendly syntax, and its elegant adaptability when deviating slightly from conventions to craft more performant patterns.

This approach unveils patterns’ intricacies when implemented in Python, bridging the gap between exhaustive resources and surface-level content. This series is tailored to those who seek an in-depth understanding without drowning in excessive detail.

Design patterns and architectural strategies
Dall-E generated image with the following concept: Design patterns and architectural strategies

So What are Design Patterns?

Definition and Purpose

Design patterns offer standard solutions for common problems encountered in software design. These patterns resemble pre-made blueprints that you can tailor to address recurring design challenges within your codebase.

Unlike plug-and-play functions or libraries, design patterns cannot be directly copied into your program. Rather, they represent general concepts for solving specific issues. By grasping the intricacies of a pattern, you can create a tailored solution that aligns with the unique requirements of your application.

A Bit of History

The concept of design patterns was popularized by the book “Design Patterns: Elements of Reusable Object-Oriented Software”, authored by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides (often referred to as the “Gang of Four” or GoF).

Published in 1994, this book introduced 23 design patterns grouped into three categories: creational, structural, and behavioral patterns. Since then, the understanding and application of design patterns have become fundamental to the field of software engineering.

Design Patterns Vs. Algorithms

Design patterns are sometimes mistaken for algorithms due to their shared trait of offering solutions to familiar problems. The central distinction lies in scope and abstraction.

Design patterns address architecture and design complexities, while algorithms navigate computational steps. Design patterns present reusable strategies for system structure, while algorithms outline a precise sequence of actions to achieve a particular objective. The implementation of the same pattern across two distinct programs can indeed differ.

Benefits of Using Design Patterns

Using design patterns offers numerous advantages to software development:

Reusability: Design patterns provide solutions that have already been refined and tested, enabling developers to reuse successful strategies for various projects.

Maintainability: Applying design patterns promotes modular and organized code, making it easier to update and maintain in the long run.

Scalability: Design patterns allow software systems to evolve gracefully, accommodating changes without causing widespread disruptions.

Consistency: Using design patterns establishes a common vocabulary and approach for developers, fostering clear communication and collaboration.

Efficiency: Design patterns address common challenges efficiently, reducing development time and the potential for errors.

Categories of Design Patterns

Design patterns exhibit diversity in complexity, detail, and applicability across a system’s scope. At the foundational level are idiomatic patterns, specific to a single programming language.

Ascending in scope, are the architectural patterns. Applicable across various languages, these patterns facilitate the design of an entire application’s architecture.

Classification also occurs based on intent:

  1. Creational Patterns: Facilitate flexible object creation and code reuse.
  2. Structural Patterns: Assembling objects and classes into larger structures while retaining their adaptability and efficiency
  3. Behavioral Patterns: Efficient interaction and allocation of responsibilities between objects, ensuring effective communication.

With this classification insight, harnessing the power of design patterns becomes a more directed endeavor in solving software challenges.

In the following sections, we will introduce each category and explore shortly representative design patterns.

Creational design patterns creating new entities
Dall-E generated image with the following concept: Creational design patterns creating new entities

Creational Patterns

  1. Singleton Pattern: Ensures that a class has only one instance and provides a global point of access to that instance.
  2. Factory Method Pattern: Defines an interface for creating objects, allowing subclasses to decide which class to instantiate.
  3. Abstract Factory Pattern: Provides an interface for creating families of related or dependent objects without specifying their concrete classes.
  4. Builder Pattern: Separates the construction of a complex object from its representation, allowing the same construction process to create different representations.
  5. Prototype Pattern: Creates new objects by copying an existing object, avoiding the overhead of creating objects from scratch.

Structural Patterns

  1. Adapter Pattern: Converts the interface of a class into another interface that clients expect, enabling classes with incompatible interfaces to work together.
  2. Bridge Pattern: Decouples an abstraction from its implementation, allowing both to evolve independently.
  3. Composite Pattern: Composes objects into tree structures to represent part-whole hierarchies, making it easier to work with individual objects and compositions.
  4. Decorator Pattern: Dynamically adds responsibilities to objects, providing a flexible alternative to subclassing for extending functionality.
  5. Facade Pattern: Provides a simplified interface to a complex subsystem, making it easier to use and understand.
  6. Flyweight Pattern: Shares instances of objects to support large numbers of fine-grained objects efficiently.
  7. Proxy Pattern: provide a substitute or placeholder for another object to control access to the original object.

Behavioral Patterns

  1. Chain of Responsibility Pattern: Creates a chain of objects that can handle requests, avoiding coupling the sender with its receivers.
  2. Command Pattern: Turns a request into a stand-alone object, allowing parameterization of clients with different requests.
  3. Interpreter Pattern: Defines a grammar for a language and an interpreter to interpret sentences in the language.
  4. Iterator Pattern: Provides a way to access elements of a collection without exposing its underlying representation.
  5. Mediator Pattern: Defines an object that centralizes communication between multiple objects, reducing direct dependencies between them.
  6. Memento Pattern: Captures and restores an object’s internal state, allowing it to be restored to a previous state.
  7. Observer Pattern: Defines a dependency between objects, ensuring that when one object changes state, all its dependents are notified and updated automatically.
  8. State Pattern: Allows an object to change its behavior when its internal state changes, enabling cleaner, more maintainable conditional logic.
  9. Strategy Pattern: Defines a family of algorithms, encapsulates each one and makes them interchangeable. Clients can choose an algorithm from this family without modifying their code.
  10. Template Method Pattern: Defines the structure of an algorithm in a superclass but lets subclasses override specific steps of the algorithm.
  11. Visitor Pattern: Separates an algorithm from an object structure, allowing new operations to be added without modifying the objects themselves.

Applying Design Patterns

Applying design patterns is an art that hinges on recognizing the right scenarios for their use. Design patterns should match the complexity of the problem — applying them to simple issues can overcomplicate while ignoring them for complex problems can hinder maintainability.

Selecting an appropriate design pattern demands a deep understanding of both the problem domain and the pattern’s principles. Strive for natural alignment; forcing a pattern where it doesn’t fit can lead to future complications. Long-term consequences should also be weighed, as a quick solution might not translate well into scalability or maintenance.

Dall-E generated image with the following concept: Python as the programming language for design patterns

Common Misconceptions

Misconceptions often cloud the understanding of design patterns, hindering their optimal utilization. One fallacy is the belief that design patterns can magically fix poor code. Design patterns enhance well-structured code, but they can’t rescue fundamentally flawed designs. Fixing underlying issues precedes applying design patterns.

Another misconception views design patterns as rigid templates to be followed blindly. While patterns offer guidance, adaptability is key. Flexibility in pattern application ensures alignment with specific project needs. Patterns are tools, not mandates, and their usage should be pragmatic and attuned to the project’s unique goals.

Criticisms of Design Patterns

Computer science critics express concerns about design patterns, Here are some of these criticisms:

Misalignment with Root Issue: Some argue that patterns arise due to limitations in the abstraction of certain languages. Concepts should be referenced, not duplicated, rendering patterns unnecessary. Paul Graham’s “Revenge of the Nerds” essay underscores this.

Absence of Formal Grounding: Critics decry design patterns’ informal study and call for structure. At OOPSLA 1999, the Gang of Four faced mock trials, facing allegations of “crimes” against computer science.

Inefficiencies in Solution: Critics claim patterns lead to code duplication. Well-factored implementations are preferred over “barely good enough” patterns.

Insufficient Distinction from Abstractions: Critics argue patterns aren’t distinct from other abstractions. The introduction of architecture-derived terminology is deemed redundant. The Model-View-Controller paradigm is cited as an example, predating design patterns, valued for its pattern language documentation.

While critics highlight concerns, I stand as a proponent of design patterns. Despite debates, understanding and implementing design patterns offer valuable tools for software developers, And I think the knowledge and strategic use of patterns contribute significantly to enhanced code organization, maintainability, and scalability.

Finally, Where We’re Going with the Series

We’ve dissected the essence of design patterns, and their variations, and even examined their limitations. Now, as I revisit the Design Patterns book, I’m excited to present a dedicated article for each pattern within this series. Join me on this journey as we deepen our grasp of patterns. Python serves as our companion, selected for its object-oriented prowess, user-friendly syntax, and the elegance it brings to crafting high-performance patterns.

Explore the GitHub Repo 🎉

Check out all code examples and design pattern implementations in the following GitHub repository. Feel free to navigate and use the samples, and contributions to add more examples are warmly welcomed.

Next on the Series 🚀

Read More 📜

References

  1. Design Patterns: Elements of Reusable Object-Oriented Software (Book)
  2. Head First Design Patterns (Book)
  3. Python Design Patterns
  4. refactoring.guru Design Patterns

--

--

Amir Lavasani

I delve into machine learning 🤖 and software architecture 🏰 to enhance my expertise while sharing insights with Medium readers. 📃