← Back to all projects

LEARN DESIGN PATTERNS

Learn Design Patterns: From Novice to Software Architect

Goal: Deeply understand software design patterns—from the fundamental “why” to building complex, maintainable, and elegant systems that leverage these proven solutions.


Why Learn Design Patterns?

Design patterns are the collected wisdom of thousands of brilliant software engineers. They are not specific algorithms or libraries, but rather high-level, reusable solutions to commonly occurring problems within a given context in software design. Most developers treat them as academic concepts, but true mastery of patterns transforms you from a coder into an architect.

After completing these projects, you will:

  • Speak a common language with other developers, making collaboration more efficient.
  • Build systems that are more flexible, reusable, and easier to maintain.
  • Recognize common problems and immediately know a set of proven ways to solve them.
  • Understand the trade-offs between different solutions.
  • Write code that is elegant, professional, and robust.

Core Concept Analysis

The Three Families of Patterns (Gang of Four)

The most famous catalog of patterns comes from the book “Design Patterns: Elements of Reusable Object-Oriented Software” by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides (the “Gang of Four”). They are organized into three families.

┌─────────────────────────────────────────────────────────────────────────┐
│                           DESIGN PATTERNS                               │
│                                                                         │
├──────────────────────┬───────────────────────┬──────────────────────────┤
│    CREATIONAL        │      STRUCTURAL       │        BEHAVIORAL        │
│ (How to create objs) │ (How to compose objs) │ (How objs communicate)   │
├──────────────────────┼───────────────────────┼──────────────────────────┤
│ • Singleton          │ • Adapter             │ • Observer               │
│ • Factory Method     │ • Decorator           │ • Strategy               │
│ • Abstract Factory   │ • Composite           │ • Command                │
│ • Builder            │ • Facade              │ • Iterator               │
│ • Prototype          │ • Proxy               │ • State                  │
│                      │ • Bridge              │ • Template Method        │
│                      │ • Flyweight           │ • Chain of Responsibility│
│                      │                       │ • Visitor, Memento,      │
│                      │                       │   Mediator, Interpreter  │
└──────────────────────┴───────────────────────┴──────────────────────────┘

Key Design Principles (The “Why” Behind the Patterns)

Patterns are manifestations of deeper principles. Understanding these principles is more important than memorizing patterns.

Principle Description Example Pattern
Encapsulate What Varies Find the parts of your code that change and separate them from what stays the same. Strategy
Program to an Interface… …not an Implementation. Depend on abstractions, not concrete classes. Factory Method
Favor Composition… …over Inheritance. Build complex behavior by assembling objects, not from a rigid class hierarchy. Decorator
Strive for Loose Coupling Minimize dependencies between objects. Changes in one shouldn’t break others. Observer
Open-Closed Principle Classes should be open for extension, but closed for modification. Decorator
Dependency Inversion High-level modules should not depend on low-level ones. Both should depend on abstractions. Abstract Factory

Project List

The following 12 projects will guide you from basic creational patterns to complex behavioral systems, teaching you to think in patterns.


Project 1: Application Configuration Manager

  • File: LEARN_DESIGN_PATTERNS.md
  • Main Programming Language: Java
  • Alternative Programming Languages: C#, Python, C++
  • Coolness Level: Level 1: Pure Corporate Snoozefest
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 1: Beginner
  • Knowledge Area: Object Creation / Global State Management
  • Software or Tool: A simple console application
  • Main Book: “Head First Design Patterns” by Eric Freeman & Elisabeth Robson

What you’ll build: A thread-safe Configuration class that loads settings from a .properties or .json file and provides global access to them, ensuring only one instance of the manager ever exists.

Why it teaches design patterns: This is the classic, canonical example for the Singleton pattern. It forces you to think about instance control, global access points, and the potential pitfalls of shared state, such as thread safety.

Core challenges you’ll face:

  • Ensuring a single instance → maps to private constructors and static access methods
  • Lazy vs. eager instantiation → maps to performance and startup time trade-offs
  • Thread safety → maps to double-checked locking or using an initialization-on-demand holder
  • Reading from a file → maps to basic file I/O and parsing

Key Concepts:

  • Singleton Pattern: “Head First Design Patterns” Chapter 5
  • Static vs. Instance Members: Java documentation on class members
  • Thread Safety: “Effective Java” by Joshua Bloch, Item 83

Difficulty: Beginner Time estimate: A few hours Prerequisites: Basic knowledge of a class-based language (Java, C#).

Real world outcome:

$ java -jar config-app.jar
Loading configuration from 'app.properties'...
Configuration loaded.
Database URL: jdbc:mysql://localhost:3306/prod
API Key: a1b2c3d4-e5f6-7890-ghij-klmnopqrstuv
Max Connections: 10

# Running again shows it doesn't reload
$ java -jar config-app.jar
Using existing configuration instance.
Database URL: jdbc:mysql://localhost:3306/prod
...

Implementation Hints:

  1. Make the constructor private to prevent direct instantiation with new.
  2. Create a private static field to hold the single instance.
  3. Create a public static method (e.g., getInstance()) that acts as the global access point.
  4. Inside getInstance(), check if the instance is null. If it is, create it. Then, always return the instance.
  5. Consider thread safety: what happens if two threads call getInstance() at the same time when the instance is null? You might need to use synchronized or a more advanced technique.

Learning milestones:

  1. Basic Singleton works → You understand instance control.
  2. Configuration loads from a file → The singleton has a real-world purpose.
  3. It is thread-safe → You understand concurrent access to a singleton.

Project 2: Document Converter Tool

  • File: LEARN_DESIGN_PATTERNS.md
  • Main Programming Language: Java
  • Alternative Programming Languages: Python, C#, Go
  • Coolness Level: Level 2: Practical but Forgettable
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 2: Intermediate
  • Knowledge Area: Creational Patterns / Behavioral Patterns
  • Software or Tool: Command-line application
  • Main Book: “Design Patterns: Elements of Reusable Object-Oriented Software” by GoF

What you’ll build: A command-line tool that takes a document (e.g., Markdown) and converts it to different formats (HTML, Plain Text, LaTeX) by using different conversion algorithms interchangeably.

Why it teaches design patterns: It combines two powerful patterns. The Factory Method lets subclasses decide which converter object to create, decoupling your main logic from concrete converter classes. The Strategy pattern lets you easily switch out the conversion algorithm at runtime.

Core challenges you’ll face:

  • Decoupling creator from product → maps to using an abstract ConverterFactory
  • Defining a family of algorithms → maps to creating a common ConversionStrategy interface
  • Encapsulating algorithms → maps to implementing concrete strategies like HtmlConversionStrategy
  • Switching strategies at runtime → maps to passing the desired strategy to a context object

Key Concepts:

  • Factory Method Pattern: Refactoring.Guru - Factory Method
  • Strategy Pattern: “Head First Design Patterns” Chapter 1
  • Program to an Interface: “Head First Design Patterns” Chapter 1

Difficulty: Intermediate Time estimate: Weekend Prerequisites: Project 1, understanding of interfaces and polymorphism.

Real world outcome:

$ java -jar converter.jar my_doc.md --to=html
Creating HTML converter...
Applying HTML conversion strategy...
Output written to 'my_doc.html'.

$ java -jar converter.jar my_doc.md --to=text
Creating Text converter...
Applying Text conversion strategy...
Output written to 'my_doc.txt'.

Implementation Hints:

  1. Strategy: Define an interface ConversionStrategy with a method convert(String document). Create concrete classes like HtmlStrategy, TextStrategy.
  2. Context: Create a DocumentConverter class that holds a reference to a ConversionStrategy. Its performConversion() method will delegate the work to the strategy object.
  3. Factory Method: Create an abstract ConverterFactory class with a method createConverter(). Concrete factories like HtmlConverterFactory will implement this method to return a DocumentConverter pre-configured with an HtmlStrategy.
  4. Your main method will select the appropriate factory based on command-line arguments, use it to create a converter, and then run the conversion.

Learning milestones:

  1. Strategies can be swapped → You understand encapsulating algorithms.
  2. Factories create configured objects → You understand decoupling object creation.
  3. The system is extensible → You can add a new format (e.g., XML) by just adding a new Strategy and Factory, without touching existing code.

Project 3: A Build-Your-Own Pizza API

  • File: LEARN_DESIGN_PATTERNS.md
  • Main Programming Language: C#
  • Alternative Programming Languages: Java, Python, TypeScript
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 2. The “Micro-SaaS / Pro Tool”
  • Difficulty: Level 2: Intermediate
  • Knowledge Area: Creational Patterns / Structural Patterns
  • Software or Tool: A simple web API or console app.
  • Main Book: “Head First Design Patterns”

What you’ll build: An API for ordering a pizza where you can construct a complex Pizza object step-by-step (crust, sauce, size) and then add toppings (cheese, pepperoni, onions) dynamically.

Why it teaches design patterns: A perfect combination of Builder and Decorator. The Builder pattern simplifies the creation of a complex object with many optional parts, avoiding a “telescoping constructor”. The Decorator pattern allows you to add responsibilities (toppings) to an object (the pizza) dynamically and transparently.

Core challenges you’ll face:

  • Constructing a complex object → maps to creating a PizzaBuilder with methods like setCrust(), setSauce()
  • Separating construction from representation → maps to the builder produces the final Pizza object only at the end
  • Adding behavior dynamically → maps to wrapping a Pizza object in one or more ToppingDecorator objects
  • Maintaining a common interface → maps to decorators and the base pizza sharing the same interface (e.g., getCost(), getDescription())

Key Concepts:

  • Builder Pattern: “Effective Java” by Joshua Bloch, Item 2
  • Decorator Pattern: “Head First Design Patterns” Chapter 3
  • Composition over Inheritance: Core principle demonstrated by the Decorator pattern.

Difficulty: Intermediate Time estimate: Weekend Prerequisites: Understanding of classes, interfaces, and composition.

Real world outcome: A console app that produces this output:

// Code:
Pizza pizza = new PizzaBuilder("Large")
                .withCrust("Thin")
                .withSauce("Tomato")
                .build();
pizza = new Cheese(pizza);
pizza = new Pepperoni(pizza);

System.out.println(pizza.getDescription() + " $" + pizza.getCost());

// Output:
Large Thin Crust Pizza with Tomato Sauce, Cheese, Pepperoni $14.50

Implementation Hints:

  1. Pizza Class: Make the Pizza class have a constructor that takes a PizzaBuilder. Its properties (crust, sauce) should be private.
  2. Builder: Create a nested static PizzaBuilder class inside Pizza. It will have methods like withCrust(), withSauce(), and a final build() method that creates the Pizza instance.
  3. Decorator Base: Create an abstract ToppingDecorator class that implements the same interface as Pizza (e.g., IPizza). It should hold a reference to the IPizza object it wraps.
  4. Concrete Decorators: Create classes like Cheese and Pepperoni that extend ToppingDecorator. Their getCost() methods should call the wrapped pizza’s getCost() and add their own cost. Their getDescription() should do the same.

Learning milestones:

  1. Complex Pizza objects are created easily → You’ve mastered the Builder pattern.
  2. Toppings can be added in any order → You’ve mastered the Decorator pattern.
  3. The final cost and description are calculated correctly → You understand how decorators chain calls.

Project 4: A Mini GUI Framework

  • File: LEARN_DESIGN_PATTERNS.md
  • Main Programming Language: Python
  • Alternative Programming Languages: Java, C#
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 3: Advanced
  • Knowledge Area: Structural Patterns / Behavioral Patterns
  • Software or Tool: A simple graphical library (like Tkinter or Pygame) or just console output.
  • Main Book: “Design Patterns: Elements of Reusable Object-Oriented Software” by GoF

What you’ll build: A simple framework for building a user interface where UI elements (Panels, Buttons, TextFields) can be nested inside each other, and elements can react to events like “click”.

Why it teaches design patterns: This is the textbook application of the Composite and Observer patterns. Composite allows you to treat a single Button and a Panel full of buttons in the same way (a part-whole hierarchy). Observer allows UI elements to subscribe to events from other elements without being tightly coupled.

Core challenges you’ll face:

  • Treating individual objects and compositions uniformly → maps to a shared Component interface for both leaf nodes (Button) and composites (Panel)
  • Building tree structures → maps to a Panel class with add() and remove() methods for child components
  • Notifying objects of state changes → maps to a Subject (the button) that notifies registered Observers (listeners)
  • Decoupling the sender from the receiver → maps to listeners implementing an Observer interface without the button knowing their concrete types

Key Concepts:

  • Composite Pattern: “Head First Design Patterns” Chapter 9
  • Observer Pattern: “Head First Design Patterns” Chapter 2
  • Part-Whole Hierarchies: The core concept of the Composite pattern.

Difficulty: Advanced Time estimate: 1-2 weeks Prerequisites: Project 2, solid understanding of object composition and events.

Real world outcome: A simple application that draws a UI tree to the console and handles events.

# Code:
root = Panel("RootPanel")
panel1 = Panel("SubPanel1")
button1 = Button("OK")
button2 = Button("Cancel")

# Listeners
button1.add_observer(lambda: print("OK button was clicked!"))
button2.add_observer(lambda: print("Cancel button was clicked!"))

root.add(panel1)
panel1.add(button1)
root.add(button2)

root.draw()
# Simulating a click
button1.click()
# Console Output:
--- Panel: RootPanel ---
  --- Panel: SubPanel1 ---
    [Button: OK]
  --- End Panel: SubPanel1 ---
  [Button: Cancel]
--- End Panel: RootPanel ---

OK button was clicked!

Implementation Hints:

  1. Composite: Define a Component interface with methods like draw() and click(). Create a Button (leaf) class and a Panel (composite) class that both implement Component. The Panel’s draw() method should iterate and call draw() on its children.
  2. Observer: Create an Observer interface with a method update(). Create a Subject class (or have the Button implement it) with add_observer(), remove_observer(), and notify_observers() methods.
  3. When a button’s click() method is called, it should in turn call notify_observers(), which loops through its registered observers and calls their update() method.

Learning milestones:

  1. You can build nested UI structures → You understand the Composite pattern.
  2. Clicking a button triggers an action in a separate object → You understand the Observer pattern.
  3. The code is clean and scalable → You can add new components and listeners without major refactoring.

Project 5: Smart Home Hub

  • File: LEARN_DESIGN_PATTERNS.md
  • Main Programming Language: Python
  • Alternative Programming Languages: Java, C#, Go
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 2. The “Micro-SaaS / Pro Tool”
  • Difficulty: Level 2: Intermediate
  • Knowledge Area: Structural Patterns
  • Software or Tool: Console application simulating smart devices.
  • Main Book: “Design Patterns: Elements of Reusable Object-Oriented Software” by GoF

What you’ll build: A central hub that controls a variety of smart home devices (lights, thermostats, coffee machines), some of which have incompatible interfaces. The hub will also provide simple “scene” commands like “Wake Up” or “Movie Night”.

Why it teaches design patterns: This project demonstrates two key structural patterns. The Adapter pattern is used to make incompatible device interfaces work with your standard hub interface. The Facade pattern provides a simple, unified interface to a complex subsystem (e.g., the sequence of calls needed for “Movie Night”).

Core challenges you’ll face:

  • Integrating incompatible interfaces → maps to creating an adapter class that translates calls, e.g., NewLightAdapter for a LegacyLight
  • Simplifying a complex subsystem → maps to a HomeTheaterFacade with a watchMovie() method that calls multiple underlying device methods
  • Providing a consistent client experience → maps to the hub only interacting with its own standard ISmartDevice interface

Key Concepts:

  • Adapter Pattern: “Head First Design Patterns” Chapter 7
  • Facade Pattern: “Head First Design Patterns” Chapter 7
  • Principle of Least Knowledge: A key idea behind the Facade pattern.

Difficulty: Intermediate Time estimate: Weekend Prerequisites: Understanding of interfaces and composition.

Real world outcome:

$ python hub.py movie_night
Initializing Movie Night scene...
  - Dimming lights to 20%
  - Lowering thermostat to 68 degrees
  - Turning on TV
  - Setting input to HDMI 2
Movie Night scene is active.

$ python hub.py control light1 on
Turning light1 on.

Implementation Hints:

  1. Define a standard interface ISmartDevice with methods like turn_on(), turn_off().
  2. Create concrete device classes like SmartLight that implement this interface.
  3. Create a “legacy” or “third-party” device class, e.g., AcmeThermostat, with a different interface (e.g., set_temperature_f()).
  4. Adapter: Create an AcmeThermostatAdapter that implements ISmartDevice. Its methods will internally call the corresponding methods on the AcmeThermostat object it wraps.
  5. Facade: Create a HomeFacade class. Give it a method start_movie_night(). This method will hold references to all the necessary devices and call their methods in the correct sequence.

Learning milestones:

  1. You can control all devices through a single interface → You’ve mastered the Adapter pattern.
  2. Complex scenes are triggered by a single command → You’ve mastered the Facade pattern.
  3. The system is easy to extend with new devices and scenes → You understand how these patterns improve maintainability.

Project 6: A Text Editor with Undo/Redo

  • File: LEARN_DESIGN_PATTERNS.md
  • Main Programming Language: Java
  • Alternative Programming Languages: C#, C++, Python
  • Coolness Level: Level 4: Hardcore Tech Flex
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 3: Advanced
  • Knowledge Area: Behavioral Patterns
  • Software or Tool: Console-based text editor.
  • Main Book: “Design Patterns: Elements of Reusable Object-Oriented Software” by GoF

What you’ll build: A simple console application that acts as a text editor. You can type text, and crucially, you can issue “undo” and “redo” commands to navigate the history of your changes.

Why it teaches design patterns: This is the ultimate project for the Command and Memento patterns. Command encapsulates each action (like “insert character” or “delete word”) into an object. Memento provides the ability to save and restore the state of the document, allowing the commands to perform their undo/redo operations.

Core challenges you’ll face:

  • Encapsulating requests as objects → maps to creating an ICommand interface with execute() and undo() methods
  • Supporting undoable operations → maps to the undo() method reversing the execute() action
  • Saving and restoring an object’s state → maps to a Document creating a DocumentMemento object that stores its content
  • Managing a history of commands → maps to using two stacks, one for undo history and one for redo history

Key Concepts:

  • Command Pattern: “Head First Design Patterns” Chapter 6
  • Memento Pattern: Refactoring.Guru - Memento
  • State Management: The core problem solved by Memento.

Difficulty: Advanced Time estimate: 1-2 weeks Prerequisites: Project 2, comfortable with data structures like Stacks.

Real world outcome: A running console application:

> type Hello
Document: 'Hello'
> type  World
Document: 'Hello World'
> undo
Document: 'Hello'
> undo
Document: ''
> redo
Document: 'Hello'
> redo
Document: 'Hello World'
> quit

Implementation Hints:

  1. Document Class (Originator): This class holds the text content. It will have a method createMemento() that saves its current state to a Memento object, and a restore(Memento m) method to set its state from a memento.
  2. Memento Class: A simple object that stores the state of the document (e.g., the text string). It should have a getState() method but no public setter.
  3. Command Interface: ICommand with execute() and undo() methods.
  4. Concrete Command: e.g., TypeCommand. Its constructor takes the text to add. execute() appends the text. undo() removes it. The command should capture a Memento of the document’s state before it executes. The undo method uses that memento to restore the state.
  5. Invoker: A History class that holds two stacks: undoStack and redoStack. When a command is executed, it’s pushed onto the undoStack. When “undo” is called, it pops a command, calls its undo() method, and pushes it to the redoStack.

Learning milestones:

  1. Actions are encapsulated in command objects → You understand the Command pattern.
  2. You can save and restore the document’s state → You understand the Memento pattern.
  3. A full undo/redo history works perfectly → You can orchestrate multiple patterns to solve a complex problem.

Project 7: A File System Analyzer

  • File: LEARN_DESIGN_PATTERNS.md
  • Main Programming Language: Python
  • Alternative Programming Languages: Java, C#, Go
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 2. The “Micro-SaaS / Pro Tool”
  • Difficulty: Level 3: Advanced
  • Knowledge Area: Behavioral Patterns / Structural Patterns
  • Software or Tool: Command-line tool that operates on the local filesystem.
  • Main Book: “Design Patterns: Elements of Reusable Object-Oriented Software” by GoF

What you’ll build: A tool that traverses a directory tree and performs various operations on the files and folders, such as calculating total size, finding the largest file, or generating a tree-view report, without changing the file/folder classes.

Why it teaches design patterns: This project elegantly combines the Composite, Iterator, and Visitor patterns. Composite models the file/directory tree structure. Iterator provides a way to traverse this structure. Visitor lets you define new operations on the structure without modifying the core file/directory objects.

Core challenges you’ll face:

  • Modeling a tree structure → maps to the Composite pattern, with File as a leaf and Directory as a composite
  • Defining new operations without changing classes → maps to the core purpose of the Visitor pattern
  • Separating an algorithm from an object structure → maps to having SizeVisitor and SearchVisitor operate on the same file/directory objects
  • Traversing the object structure → maps to having each component’s accept(Visitor v) method responsible for traversal

Key Concepts:

  • Visitor Pattern: Refactoring.Guru - Visitor
  • Composite Pattern: (See Project 4)
  • Double Dispatch: The mechanism that makes Visitor work, where the operation depends on both the type of the visitor and the type of the element.

Difficulty: Advanced Time estimate: 1-2 weeks Prerequisites: Project 4 (Composite), understanding of recursion.

Real world outcome:

$ python fs_analyzer.py . --visit=size
Analyzing directory '.' with 'size' visitor...
Total size: 4.75 GB

$ python fs_analyzer.py . --visit=find --pattern=*.py
Analyzing directory '.' with 'find' visitor...
Found: ./fs_analyzer.py
Found: ./tests/test_visitors.py
...

Implementation Hints:

  1. Component Interface: IFileSystemComponent with an accept(IVisitor visitor) method.
  2. Leaf & Composite: File and Directory classes implementing IFileSystemComponent. Directory will have a list of children.
  3. Visitor Interface: IVisitor with methods like visit_file(File f) and visit_directory(Directory d).
  4. Concrete Visitors: SizeVisitor will have a total_size property. Its visit_file method adds the file’s size. FindVisitor will check the file name against a pattern.
  5. Double Dispatch: The File.accept(visitor) method will simply call visitor.visit_file(this). The Directory.accept(visitor) method will first call visitor.visit_directory(this), and then iterate through its children, calling child.accept(visitor) on each one.

Learning milestones:

  1. You have a working Composite structure for the filesystem → You’ve reinforced your understanding of Composite.
  2. You can add a new operation (e.g., a “Permissions Report” visitor) without touching the File or Directory classes → You’ve mastered the Visitor pattern.
  3. The traversal logic is correctly handled by the accept methods → You understand double dispatch.

Project 8: RPG Character State Machine

  • File: LEARN_DESIGN_PATTERNS.md
  • Main Programming Language: C#
  • Alternative Programming Languages: Java, C++, Python
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 2: Intermediate
  • Knowledge Area: Behavioral Patterns
  • Software or Tool: Text-based RPG game.
  • Main Book: “Head First Design Patterns”

What you’ll build: A simple text-based role-playing game where a character’s behavior (how they attack, defend, or use items) changes based on their current status (e.g., Normal, Poisoned, Enraged, Hidden).

Why it teaches design patterns: This is a classic application of the State and Template Method patterns. The State pattern allows an object (the character) to alter its behavior when its internal state changes. The Template Method defines the skeleton of an action (like TakeTurn), allowing the specific states to override certain parts of it (like ChooseAction).

Core challenges you’ll face:

  • Changing behavior based on internal state → maps to delegating behavior from the Character class to a current IState object
  • Avoiding massive if/else or switch statements → maps to the core problem solved by the State pattern
  • Defining a skeleton of an algorithm → maps to a base CharacterAction class with a perform() template method
  • Letting subclasses redefine certain steps → maps to states overriding methods like get_available_actions()

Key Concepts:

  • State Pattern: “Head First Design Patterns” Chapter 10
  • Template Method Pattern: “Head First Design Patterns” Chapter 8
  • Finite State Machines: The underlying computer science concept.

Difficulty: Intermediate Time estimate: 1-2 weeks Prerequisites: Project 2, comfortable with object-oriented concepts.

Real world outcome: A running console game:

Player (Health: 100/100, State: Normal) encounters a Goblin!
Your turn. Actions: [Attack, Defend]
> Attack
Player attacks Goblin for 12 damage!

Goblin attacks Player for 8 damage. Player is POISONED!
Player (Health: 92/100, State: Poisoned)
Your turn. Actions: [Struggle, UseAntidote]
> Struggle
Player struggles, dealing 4 damage! Player takes 5 poison damage.

Implementation Hints:

  1. Context Class: The PlayerCharacter class. It holds a reference to its current IState object. Methods like Attack() or Defend() will delegate to the current state object: _currentState.Attack(this, target). It also needs a SetState() method.
  2. State Interface: ICharacterState with methods for all possible actions (Attack, Defend, UseItem, etc.).
  3. Concrete States: NormalState, PoisonedState, EnragedState. Each implements the ICharacterState interface differently. For example, PoisonedState.Attack() might do less damage than NormalState.Attack().
  4. Template Method: You could have an abstract Turn class with a template method execute(). The steps would be beforeTurnEffects(), chooseAction(), afterTurnEffects(). The PoisonedState could add poison damage in the afterTurnEffects() step.

Learning milestones:

  1. The character’s available actions and their outcomes change based on its state object → You’ve mastered the State pattern.
  2. You can easily add new states (e.g., ConfusedState) without modifying the PlayerCharacter class → You understand the power of state-based delegation.
  3. Common algorithms are defined in a base class while specifics are handled by subclasses/states → You’ve grasped the Template Method pattern.

Project 9: Cross-Platform UI Toolkit

  • File: LEARN_DESIGN_PATTERNS.md
  • Main Programming Language: C++
  • Alternative Programming Languages: Java, C#, Rust
  • Coolness Level: Level 4: Hardcore Tech Flex
  • Business Potential: 4. The “Open Core” Infrastructure
  • Difficulty: Level 4: Expert
  • Knowledge Area: Structural Patterns / Creational Patterns
  • Software or Tool: Console application simulating UI rendering.
  • Main Book: “Design Patterns: Elements of Reusable Object-Oriented Software” by GoF

What you’ll build: A system that can create and render a user interface (buttons, windows, menus) for multiple “operating systems” (e.g., Windows, macOS, Linux), where the abstract UI elements are decoupled from their platform-specific implementations.

Why it teaches design patterns: A powerful demonstration of the Abstract Factory and Bridge patterns working together. Abstract Factory provides an interface for creating families of related UI elements (WindowsFactory, MacFactory). Bridge decouples the high-level UI element abstractions (Window, Button) from their low-level platform implementations (WindowsWindowImpl, MacWindowImpl).

Core challenges you’ll face:

  • Creating families of related objects → maps to the GUIFactory interface creating both buttons and windows
  • Decoupling abstraction from implementation → maps to the Window class holding a reference to a WindowImpl interface, not a concrete class
  • Allowing abstraction and implementation to vary independently → maps to you can create a RefinedWindow without touching implementation, or a GTKWindowImpl without touching the abstraction
  • Enforcing that a UI uses elements from only one theme → maps to the Abstract Factory ensuring a WindowsFactory only creates Windows-compatible elements

Key Concepts:

  • Abstract Factory Pattern: “Head First Design Patterns” Chapter 4
  • Bridge Pattern: Refactoring.Guru - Bridge
  • Decoupling: The primary goal of both patterns.

Difficulty: Expert Time estimate: 2-3 weeks

  • Prerequisites: Projects 2 & 5. Strong grasp of abstraction, interfaces, and pointers/references.

Real world outcome:

// main.cpp
int main() {
    // Environment variable or config determines the factory
    GUIFactory* factory = new MacFactory();

    Application app(factory);
    app.run(); // Creates and renders a window with a button

    // ... clean up ...
    return 0;
}
// Console Output:
Creating UI with MacFactory...
  Drawing a native macOS window border.
  Drawing a native Aqua-style button.

Implementation Hints:

  1. Bridge (Implementation): Create an IWindowImpl interface with methods like draw_border(). Create concrete classes WindowsWindowImpl, MacWindowImpl.
  2. Bridge (Abstraction): Create an abstract Window class that holds a pointer to IWindowImpl. Its draw() method calls _impl->draw_border(). You can have refined abstractions like DialogWindow.
  3. Abstract Factory: Create an IGUIFactory interface with methods create_window() and create_button().
  4. Concrete Factories: WindowsFactory will create Window objects equipped with a WindowsWindowImpl, and Button objects with a WindowsButtonImpl. MacFactory does the same for macOS.
  5. Your client code (the Application) is configured with a factory. It asks the factory to create UI elements, blissfully unaware of the concrete OS-specific classes being used.

Learning milestones:

  1. You can switch the entire look-and-feel of the application by changing only the factory → You’ve mastered Abstract Factory.
  2. The high-level Window logic is completely separate from the low-level drawing code → You’ve mastered the Bridge pattern.
  3. You can add a new UI element (TextBox) or a new OS (Linux) by adding new classes without a cascade of changes → You understand true decoupling.

Project 10: Command-Line Chat Room

  • File: LEARN_DESIGN_PATTERNS.md
  • Main Programming Language: Go
  • Alternative Programming Languages: Java (with threads), Python (with asyncio), C#
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 2. The “Micro-SaaS / Pro Tool”
  • Difficulty: Level 3: Advanced
  • Knowledge Area: Behavioral Patterns
  • Software or Tool: Concurrent console application.
  • Main Book: “Design Patterns: Elements of Reusable Object-Oriented Software” by GoF

What you’ll build: A multi-user chat application where User objects communicate with each other, but not directly. All communication is routed through a central ChatRoom object. You can also add a logging layer that intercepts messages.

Why it teaches design patterns: This project is a great way to learn the Mediator and Proxy patterns. Mediator centralizes complex communications between multiple objects, preventing a “spaghetti” of connections. Proxy provides a surrogate or placeholder for another object to control access to it, perfect for logging, caching, or security checks.

Core challenges you’ll face:

  • Reducing coupling between many objects → maps to users only knowing about the mediator, not each other
  • Centralizing communication logic → maps to the ChatRoom (Mediator) containing the logic to route messages
  • Controlling access to an object → maps to a LoggingProxy that intercepts messages before they reach the real chat room
  • Adding behavior without changing the object’s code → maps to the proxy adding logging without modifying the ChatRoom class

Key Concepts:

  • Mediator Pattern: Refactoring.Guru - Mediator
  • Proxy Pattern: “Head First Design Patterns” Chapter 11 (Remote Proxy) & Refactoring.Guru (Protection/Logging Proxy)
  • Concurrency: Handling multiple users requires basic concurrency concepts (goroutines in Go, threads in Java).

Difficulty: Advanced Time estimate: 1-2 weeks Prerequisites: Understanding of interfaces and basic concurrency.

Real world outcome: A running terminal application where multiple users can chat.

# Terminal 1
> ./chat --user=Alice
Alice has joined.
> Hello everyone!

# Terminal 2
> ./chat --user=Bob
Alice is in the room.
Bob has joined.
[Alice]: Hello everyone!
> Hi Alice!

# Terminal 1
[Bob]: Hi Alice!

# Log file (from the proxy)
[2025-12-20 10:30:01] User Alice sending message: 'Hello everyone!'
[2025-12-20 10:30:15] User Bob sending message: 'Hi Alice!'

Implementation Hints:

  1. Colleague: Create a User class. It should hold a reference to the IChatRoomMediator. Its send() method will call the mediator’s broadcast() method. It also needs a receive() method that the mediator can call.
  2. Mediator: Create an IChatRoomMediator interface with broadcast(User, message) and register(User) methods.
  3. Concrete Mediator: The ChatRoom class implements the mediator interface. It maintains a list of registered User objects. Its broadcast method loops through the users and calls their receive method.
  4. Proxy: Create a ChatRoomProxy that also implements IChatRoomMediator. It holds a reference to the real ChatRoom object. Its broadcast method will first write to a log file and then call the real chat room’s broadcast method. Your application will interact with the proxy, not the real chat room.

Learning milestones:

  1. Users can communicate without having direct references to each other → You’ve mastered the Mediator pattern.
  2. Message traffic is logged without the ChatRoom or User classes being aware of it → You’ve mastered the Proxy pattern.
  3. The system can handle multiple concurrent users → You can apply design patterns in a concurrent environment.

Project 11: Document Processing Pipeline

  • File: LEARN_DESIGN_PATTERNS.md
  • Main Programming Language: Python
  • Alternative Programming Languages: Java, C#, Go
  • Coolness Level: Level 2: Practical but Forgettable
  • Business Potential: 3. The “Service & Support” Model
  • Difficulty: Level 2: Intermediate
  • Knowledge Area: Behavioral Patterns
  • Software or Tool: Console application for processing text files.
  • Main Book: “Design Patterns: Elements of Reusable Object-Oriented Software” by GoF

What you’ll build: A system that processes a document through a series of sequential handlers. For example, a request might first be checked by an AuthenticationHandler, then a ValidationHandler, and finally a ContentEnrichmentHandler.

Why it teaches design patterns: This is the canonical example of the Chain of Responsibility pattern. It allows you to create a chain of processing objects (handlers) and pass a request along the chain. Each handler decides whether to process the request or pass it to the next handler in the chain.

Core challenges you’ll face:

  • Decoupling sender and receiver → maps to the client only knows about the first handler in the chain
  • Allowing multiple objects to handle a request → maps to each handler having a chance to act
  • Dynamically configuring the chain → maps to linking handlers together at runtime
  • Avoiding a giant if-elif-else block → maps to the core problem solved by this pattern

Key Concepts:

  • Chain of Responsibility Pattern: Refactoring.Guru - Chain of Responsibility
  • Loose Coupling: The main benefit of the pattern.

Difficulty: Intermediate Time estimate: Weekend Prerequisites: Understanding of linked lists (conceptually) and object composition.

Real world outcome:

$ python pipeline.py document.txt --user=admin
Handler: AuthenticationHandler -> Checking credentials for 'admin'. OK.
Handler: ValidationHandler -> Validating document format. OK.
Handler: ContentEnrichmentHandler -> Adding metadata to document.
Processing complete.

$ python pipeline.py document.txt --user=guest
Handler: AuthenticationHandler -> Checking credentials for 'guest'. FAILED.
Processing stopped: Unauthorized.

Implementation Hints:

  1. Request Object: Create a DocumentRequest class that holds the document content, user credentials, etc. This object will be passed down the chain.
  2. Handler Interface: Create an abstract Handler class or interface. It should have a field for the _next_handler and two main methods: set_next(handler) to build the chain, and handle(request).
  3. Handler Implementation: The handle method in the abstract base class should contain the core logic: if there’s a next handler, call self._next_handler.handle(request).
  4. Concrete Handlers: Create classes like AuthenticationHandler and ValidationHandler that inherit from Handler. They override the handle method. Inside, they perform their specific logic. If they can handle the request and processing should continue, they call super().handle(request) to pass it on. If processing should stop, they simply return.
  5. Client: The client code builds the chain: h1 = AuthHandler(), h2 = ValidHandler(), h1.set_next(h2). Then it kicks off the process by calling h1.handle(request).

Learning milestones:

  1. Requests flow through a chain of handlers → You understand the basic structure of the pattern.
  2. Any handler can stop the chain → You understand conditional request passing.
  3. You can reorder or add new handlers without changing client code → You’ve mastered the flexibility of the pattern.

Project 12: High-Performance Particle System

  • File: LEARN_DESIGN_PATTERNS.md
  • Main Programming Language: C++
  • Alternative Programming Languages: Rust, Java with a graphics library
  • Coolness Level: Level 4: Hardcore Tech Flex
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 4: Expert
  • Knowledge Area: Structural Patterns / Performance Optimization
  • Software or Tool: A simple graphics library (like SFML or SDL) or ASCII art rendering.
  • Main Book: “Game Programming Patterns” by Robert Nystrom

What you’ll build: A simulation that renders tens of thousands of particles (e.g., rain, snow, sparks) on the screen at a high frame rate by minimizing memory usage.

Why it teaches design patterns: This is the classic use case for the Flyweight pattern. The goal of Flyweight is to fit more objects into available RAM by sharing common state between multiple objects instead of storing it in each object. It’s essential for performance-critical applications.

Core challenges you’ll face:

  • Minimizing memory consumption → maps to the core goal of the Flyweight pattern
  • Separating intrinsic from extrinsic state → maps to identifying what can be shared vs. what must be unique
  • Managing a pool of shared objects → maps to creating a factory that returns existing flyweights or creates new ones
  • Passing extrinsic state to methods → maps to the client providing position/velocity to the flyweight’s draw method

Key Concepts:

  • Flyweight Pattern: “Game Programming Patterns” by Robert Nystrom - Chapter on Flyweight
  • Intrinsic vs. Extrinsic State: Intrinsic is context-independent and shareable (e.g., a particle’s color, texture). Extrinsic is context-dependent and unique (e.g., a particle’s position, velocity).
  • Object Pools: A common way to manage flyweight objects.

Difficulty: Expert Time estimate: 2-3 weeks Prerequisites: Project 9, strong grasp of memory management and performance concepts.

Real world outcome: A smooth animation of thousands of particles, where a naive implementation would be slow and choppy.

// Memory usage without Flyweight:
// 10,000 particles * (position(8) + velocity(8) + color(4) + sprite(1024)) bytes = ~10 MB

// Memory usage with Flyweight:
// 1 ParticleType (color+sprite) = ~1 KB
// 10,000 particles * (position(8) + velocity(8) + pointer(8)) bytes = ~240 KB
// Total: ~241 KB -- a massive saving!

Implementation Hints:

  1. Flyweight Class: Create a ParticleType class. This will store the intrinsic state: color, sprite/texture, base size.
  2. Flyweight Factory: Create a ParticleFactory class. It will have a method get_particle_type(color, sprite). This factory maintains a map or hash table of already-created ParticleType objects. When asked for a type, it first checks the map. If it exists, it returns the existing object; otherwise, it creates a new one, adds it to the map, and returns it.
  3. Context Class: The Particle class. This stores the extrinsic state: current position, velocity, and a reference/pointer to its ParticleType flyweight object.
  4. Client: The main game loop will hold a list of thousands of Particle objects. In the update loop, it updates each particle’s position. In the draw loop, it calls particle.draw(canvas). The Particle::draw method would look like this: _type->draw(canvas, _position). Notice how the extrinsic state (_position) is passed to the flyweight object.

Learning milestones:

  1. Shared (intrinsic) state is stored in separate objects from unique (extrinsic) state → You understand the core concept of Flyweight.
  2. A factory manages a pool of shared flyweight objects, preventing duplication → You know how to implement the pattern efficiently.
  3. Your application can handle a huge number of objects with low memory overhead → You’ve successfully applied the pattern to solve a real performance problem.

Summary

Project Main Language Patterns Learned Difficulty
1. Config Manager Java Singleton Beginner
2. Document Converter Java Factory Method, Strategy Intermediate
3. Pizza API C# Builder, Decorator Intermediate
4. Mini GUI Framework Python Composite, Observer Advanced
5. Smart Home Hub Python Adapter, Facade Intermediate
6. Text Editor w/ Undo Java Command, Memento Advanced
7. File System Analyzer Python Composite, Visitor, Iterator Advanced
8. RPG State Machine C# State, Template Method Intermediate
9. Cross-Platform UI C++ Abstract Factory, Bridge Expert
10. Chat Room Go Mediator, Proxy Advanced
11. Processing Pipeline Python Chain of Responsibility Intermediate
12. Particle System C++ Flyweight Expert