đź“„

Introduction to Design Patterns

Intermediate 3 min read 600 words

Overview of design patterns and when to use them in software development

Introduction to Design Patterns

Design patterns are reusable solutions to common problems in software design. They’re not finished code, but templates describing how to solve a problem in many different situations.

The Gang of Four (GoF)

Design patterns were popularized by the book “Design Patterns: Elements of Reusable Object-Oriented Software” (1994) by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides - known as the “Gang of Four.”

Pattern Categories

Design patterns are organized into three categories:

Creational Patterns

How objects are created

Pattern Purpose
Singleton Ensure only one instance exists
Factory Method Create objects without specifying exact class
Abstract Factory Create families of related objects
Builder Construct complex objects step by step
Prototype Clone existing objects

Structural Patterns

How objects are composed

Pattern Purpose
Adapter Make incompatible interfaces work together
Bridge Separate abstraction from implementation
Composite Treat individual objects and compositions uniformly
Decorator Add behavior dynamically
Facade Provide simplified interface to complex system
Flyweight Share common state between objects
Proxy Control access to another object

Behavioral Patterns

How objects communicate

Pattern Purpose
Chain of Responsibility Pass request along handler chain
Command Encapsulate request as object
Iterator Access elements sequentially
Mediator Centralize complex communications
Memento Capture and restore object state
Observer Notify multiple objects of changes
State Alter behavior when state changes
Strategy Encapsulate interchangeable algorithms
Template Method Define algorithm skeleton
Visitor Add operations to objects without modifying them

Pattern Selection Guide

// Quick reference for choosing patterns
public string SelectPattern(string problem) => problem switch
{
    // CREATION
    "Need single global instance" => "Singleton",
    "Create objects based on runtime conditions" => "Factory Method",
    "Create families of related objects" => "Abstract Factory",
    "Complex object construction with many options" => "Builder",
    "Clone objects efficiently" => "Prototype",

    // STRUCTURE
    "Integrate incompatible interfaces" => "Adapter",
    "Add features to objects dynamically" => "Decorator",
    "Simplify complex subsystem" => "Facade",
    "Tree-like hierarchies" => "Composite",
    "Control access to objects" => "Proxy",

    // BEHAVIOR
    "Notify multiple observers of changes" => "Observer",
    "Swap algorithms at runtime" => "Strategy",
    "Undo/redo operations" => "Command",
    "Process requests through handlers" => "Chain of Responsibility",
    "State-dependent behavior" => "State",
    "Define algorithm skeleton" => "Template Method",

    _ => "Consider if a pattern is needed"
};

Common Patterns in .NET

The .NET framework itself uses many design patterns:

Pattern .NET Examples
Singleton HttpClient (recommended usage), Console
Factory ILoggerFactory, Task.Factory
Builder StringBuilder, ConfigurationBuilder
Iterator IEnumerable<T>, foreach
Observer IObservable<T>, Events
Strategy IComparer<T>, Func<T> delegates
Decorator Stream wrappers (BufferedStream, GZipStream)
Template Method Stream.Read abstract method

When to Use Patterns

Use Patterns When:

  • You recognize a common problem they solve
  • The pattern’s complexity is justified
  • Team members understand the pattern
  • It makes the code more maintainable

Avoid Patterns When:

  • The solution is simpler without them
  • You’re adding patterns “just in case”
  • The pattern introduces unnecessary complexity
  • A straightforward approach works fine

Anti-Pattern: Over-Engineering

// BAD: Pattern for simple task
public interface IAdditionStrategy
{
    int Add(int a, int b);
}

public class StandardAdditionStrategy : IAdditionStrategy
{
    public int Add(int a, int b) => a + b;
}

public class AdditionContext
{
    private readonly IAdditionStrategy _strategy;
    public AdditionContext(IAdditionStrategy strategy) => _strategy = strategy;
    public int Execute(int a, int b) => _strategy.Add(a, b);
}

// GOOD: Simple is better
public static int Add(int a, int b) => a + b;

Pattern Documentation Format

Each pattern in this section follows this structure:

  1. Intent - What problem does it solve?
  2. Problem - When to use it
  3. Solution - How it works
  4. Structure - Class diagram
  5. Implementation - C# code example
  6. Real-World Usage - Where it’s used in practice
  7. Interview Tips - Common questions

Interview Tips

Common Questions:

  • “What design patterns have you used?”
  • “Explain [specific pattern] with an example”
  • “What’s the difference between [pattern A] and [pattern B]?”
  • “When would you NOT use a pattern?”

Key Points:

  1. Know the major patterns (Singleton, Factory, Strategy, Observer, Decorator)
  2. Have real examples from your experience
  3. Understand trade-offs (patterns add complexity)
  4. Know .NET framework examples
  5. Don’t over-engineer simple problems

The following pages cover the most important patterns in detail.