Design Patterns

Design Patterns #

Design patterns are reusable solutions to common problems in object-oriented programming.

A design pattern typically involves a small set of classes cooperating to achieve a desired end. This is done by adding a level of indirection in some clever way (with interfaces, information hiding, polymorphism, intermediary objects, etc.), and the resulting solution provides the same functionality as a simpler approach, but in a more elegant, efficient, or adaptable way.

Design Patterns Hierarchy

Think of design patterns as high-level programming abstractions. First, you learn the basics (data structures, algorithms, tools, language details). Then, you learn modules, interfaces, information hiding, classes/OO programming. Design patterns are the next level of abstraction – and above them sits software architecture.

Instead of code reuse, with design patterns you get experience reuse: other developers have faced and solved the same design problems, so you can exploit the wisdom and lessons learned.

Benefits of Design Patterns #

Benefits of Design Patterns

  • Leverage existing design knowledge. Other people have faced similar situations. Design patterns capture solutions that have been proven in practice.

  • Shared vocabulary. Patterns give developers a common language to communicate design decisions efficiently. When someone says “we used an Observer here,” the team immediately understands the structure and trade-offs involved.

  • Flexibility for change. When a maintainer looks at the code and recognizes the design pattern choices, they know what changes they can make without breaking the design.

  • And the common goal in software design: Improve cohesion and reduce coupling.

References #

  • Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software.

    • The “Gang of Four” book is the most influential book on design patterns. We will mostly follow the terminology and categorization defined in this book.
  • Eric Freeman, Elisabeth Robson. Head First Design Patterns.

    • This book is enriched with examples and illustrations, making it easier to understand the (sometimes unintuitive) design patterns.
  • There are also plenty of online resources and coding examples for design patterns. You can find more by searching the name of the pattern—one of the benefits of having a shared vocabulary.

Categories #

The Gang of Four (GoF) catalogued 23 design patterns in three categories:

  • Structural patterns concern the process of assembling objects and classes:
    • Adapter, Composite, Decorator, Facade, Bridge, Flyweight, Proxy
  • Behavioral patterns concern the interaction between classes or objects:
    • Observer, Strategy, Template Method, Iterator, State, Chain of Responsibility, Command, Mediator, Memento
  • Creational patterns concern the process of object creation:
    • Singleton, Factory Method, Abstract Factory, Builder, Prototype, Object Pool

We will go through a couple of patterns from each category in class (focusing on the ones that are frequently used). For each pattern, we introduce the motivation (the problem we want to solve), the intent (the proposed solution), and the structure (class diagram showing how the pattern is implemented).

This is of course not an exhaustive list of design patterns. There are thousands of design patterns “out there” and thousands more waiting to be discovered—some are domain-specific, some are special cases of others.

Avoiding Over-Engineering and Anti-Patterns #

It is tempting to apply many design patterns to a project, but overuse can cause system bloat and make the code harder to understand than a simpler approach would. Follow the YAGNI principle—“You Aren’t Going to Need It”:

  • Design for the system you need today.
  • Revisit the design periodically for opportunities to refactor and improve.

Anti-patterns (also called “code smells”) are the opposite of design patterns: they identify poor solutions to recurring design problems and are symptoms of poor design choices. Here are some common ones to watch out for:

  • Code Clone (Code Duplication).

    • Useful pieces of code are copy-pasted throughout the codebase. This is by far the most common code smell among new programmers. It causes a maintenance nightmare: a bug fix in one place must be propagated to all duplicated places.
    • Solution: use an abstraction technique (e.g., extract a method, use inheritance) to remove the duplication.
  • Multiple-Personality Class.

    • A class that contains disjoint sets of public methods used by disjoint sets of clients, making its purpose hard to understand.
    • Solution: refactor each set of methods into its own class.
  • The Blob.

    • A large class that lacks cohesion or makes most of the processing decisions.
    • Solution: refactor into a series of more cohesive, focused classes.
  • Data Class.

    • A class that exposes data members but provides no substantial functionality; data is manipulated elsewhere. Related data and behavior are not in the same scope (poor proximity), leading to fragile design.
    • Solution: move methods that manipulate the data into the data class itself.
  • Data Clump.

    • A large group of parameters that appear together in the signatures of many methods.
    • Solution: introduce a new class that encapsulates the parameter group.
  • Feature Envy.

    • A method that manipulates the data of another class more than its own.
    • Solution: move the method to the class whose data it uses.
  • Tradition Breaker.

    • A subclass breaks the API inherited from its superclass by overriding methods with empty implementations or reducing visibility.
    • Solution: redesign the inheritance structure so that the subclass properly substitutes for its superclass.