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:
- Intent - What problem does it solve?
- Problem - When to use it
- Solution - How it works
- Structure - Class diagram
- Implementation - C# code example
- Real-World Usage - Where it’s used in practice
- 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:
- Know the major patterns (Singleton, Factory, Strategy, Observer, Decorator)
- Have real examples from your experience
- Understand trade-offs (patterns add complexity)
- Know .NET framework examples
- Don’t over-engineer simple problems
The following pages cover the most important patterns in detail.