Phase 5 · Professional DevelopmentModule 34~48 min read

Design Patterns

Solve recurring design problems with creational, structural, and behavioral patterns.

What you'll learn

Design patterns are proven, reusable solutions to problems that come up again and again in software design. Learning them gives you a shared vocabulary with other developers and a toolbox of tried-and-tested approaches — so you don't reinvent the wheel.

By the end you'll be able to:

  • Explain why patterns matter and how they're categorised
  • Apply key creational patterns (Singleton, Factory, Builder)
  • Recognise structural patterns (Adapter, Decorator, Facade)
  • Use behavioral patterns (Strategy, Observer)
  • Know when not to reach for a pattern

The three categories

The classic "Gang of Four" catalogue groups 23 patterns into three families by what they're about. You've already met several without naming them — iterators, the builder-like fluent APIs, and the strategy-like functional interfaces:

Pattern categories

Creational

How objects are created

Singleton · Factory · Builder

Structural

How objects are composed

Adapter · Decorator · Facade

Behavioral

How objects interact

Strategy · Observer · Command

Creational patterns

These control how objects are made. Singleton ensures a class has exactly one instance with a global access point — good for shared config or a connection pool:

Singleton.java
class Config {
    private static final Config INSTANCE = new Config();  // the only one
    private Config() { }                                   // block 'new'

    public static Config get() { return INSTANCE; }
    public String appName() { return "MasterCoding"; }
}

public class Main {
    public static void main(String[] args) {
        System.out.println(Config.get().appName());   // one shared instance
    }
}

Factory methods create objects without exposing the exact class (you ask for a "shape" and get the right subclass). Builder constructs a complex object step by step with a readable, fluent API — far nicer than a constructor with ten arguments:

Builder.java
// Builder: construct a complex object step by step, readably
Pizza pizza = new Pizza.Builder()
    .size("large")
    .addTopping("cheese")
    .addTopping("mushroom")
    .thinCrust(true)
    .build();

Structural patterns

These describe how objects are composed into larger structures:

  • Adapter — wraps an incompatible interface so it fits what your code expects (a plug adapter)
  • Decorator — adds behaviour by wrapping an object, without changing its class (Java's I/O streams are decorators)
  • Facade — a simple front door to a complex subsystem
  • Proxy — a stand-in that controls access (lazy loading, caching, security)

Behavioral patterns

These govern how objects communicate. Strategy — the most useful — lets you swap an algorithm at runtime by holding it behind an interface. Notice this is exactly what functional interfaces and dependency injection enable:

Strategy.java
interface Discount { double apply(double price); }

class NoDiscount implements Discount { public double apply(double p) { return p; } }
class HalfOff    implements Discount { public double apply(double p) { return p * 0.5; } }

class Checkout {
    private final Discount discount;
    Checkout(Discount d) { this.discount = d; }          // inject the strategy
    double total(double price) { return discount.apply(price); }
}

public class Main {
    public static void main(String[] args) {
        Checkout sale = new Checkout(new HalfOff());     // swap behaviour freely
        System.out.println(sale.total(100));
    }
}

Observer lets objects subscribe to and react to events (the model behind UI listeners and event systems). Others include Command (wrap an action as an object), State, Template Method, and Iterator (which you used in every for-each loop). At a larger scale, MVC and layered architecture organise whole applications (Module 37).

When NOT to use patterns

Patterns are tools, not goals. Forcing them where they aren't needed adds complexity and hurts readability — the opposite of their purpose. Reach for a pattern when you recognise the problem it solves in your code, not because a pattern exists. A simple method often beats an elaborate pattern.

Watch out

"Pattern fever" — wrapping everything in factories and strategies just to look sophisticated — is a real anti-pattern. Start simple (KISS, YAGNI from Module 33); introduce a pattern only when the design pressure it relieves actually shows up.

Recap & quick check

Key takeaways

  • Design patterns are reusable solutions and a shared vocabulary for common design problems.
  • Creational (Singleton, Factory, Builder) control object creation.
  • Structural (Adapter, Decorator, Facade) control composition; Java I/O uses Decorator.
  • Behavioral (Strategy, Observer, Command) control interaction; Strategy swaps algorithms at runtime.
  • Use a pattern only when you recognise the problem it solves — don't force them (KISS).

Quick check

1. The Singleton pattern ensures…

2. Which category does the Builder pattern belong to?

3. What does the Strategy pattern let you do?

4. Java's I/O stream classes are a real-world example of which pattern?

5. When should you apply a design pattern?

Excellent — you've got a professional design toolbox. Next up: Module 35 — Algorithms & Data Structures.