← Back to Curriculum

Design Patterns

📚 Lesson 10 of 16 ⏱ 60 min

Design Patterns

60 min

Design patterns are proven, reusable solutions to common software design problems. They represent best practices evolved over time by experienced developers. Patterns provide a common vocabulary and enable you to solve problems using tested approaches. Understanding design patterns helps you write more maintainable, flexible, and understandable code. Patterns are not code to copy but templates to adapt to your specific needs.

Creational patterns deal with object creation mechanisms, trying to create objects in a manner suitable for the situation. Common patterns include Singleton (ensures only one instance exists), Factory (creates objects without specifying exact classes), Builder (constructs complex objects step by step), and Prototype (creates objects by cloning). These patterns provide flexibility in object creation and can improve code organization. Understanding creational patterns helps you design flexible object creation.

Structural patterns deal with object composition and relationships, enabling you to compose objects into larger structures. Common patterns include Adapter (allows incompatible interfaces to work together), Decorator (adds behavior to objects dynamically), Facade (provides simplified interface to complex subsystem), and Proxy (provides placeholder for another object). These patterns help you design flexible, extensible systems. Understanding structural patterns helps you compose objects effectively.

Behavioral patterns deal with communication between objects and responsibility distribution. Common patterns include Observer (notifies multiple objects of state changes), Strategy (defines family of algorithms, makes them interchangeable), Command (encapsulates requests as objects), and Template Method (defines algorithm skeleton, subclasses fill details). These patterns help you design flexible communication and behavior. Understanding behavioral patterns helps you design flexible, maintainable systems.

Patterns should be applied when they solve real problems, not just because they exist. Overusing patterns can make code overly complex. Patterns are tools, not goals. Understanding when to use patterns and when simpler solutions are better is crucial. Patterns work best when they're part of a thoughtful design, not when forced into every situation. Understanding pattern application helps you use them effectively.

Best practices include understanding the problem before applying patterns, choosing patterns that fit your needs, adapting patterns to your context, not overusing patterns, and documenting pattern usage. Patterns should improve code quality, not complicate it. Understanding design patterns enables you to design maintainable, flexible Java applications using proven solutions.

Key Concepts

  • Design patterns are proven solutions to common design problems.
  • Creational patterns deal with object creation mechanisms.
  • Structural patterns deal with object composition and relationships.
  • Behavioral patterns deal with communication between objects.
  • Patterns should be applied when they solve real problems.

Learning Objectives

Master

  • Understanding common design patterns and their use cases
  • Implementing creational, structural, and behavioral patterns
  • Recognizing when to apply design patterns
  • Adapting patterns to specific contexts

Develop

  • Software design thinking and pattern recognition
  • Understanding when patterns help and when they don't
  • Designing maintainable, flexible systems

Tips

  • Understand the problem before applying patterns.
  • Choose patterns that fit your needs, don't force them.
  • Adapt patterns to your context—don't copy blindly.
  • Don't overuse patterns—simpler solutions are often better.

Common Pitfalls

  • Applying patterns everywhere, making code overly complex.
  • Not understanding patterns, implementing them incorrectly.
  • Using patterns when simpler solutions would work.
  • Treating patterns as goals rather than tools.

Summary

  • Design patterns are proven solutions to common design problems.
  • Creational patterns handle object creation.
  • Structural patterns handle object composition.
  • Behavioral patterns handle object communication.
  • Understanding patterns enables better software design.

Exercise

Implement several design patterns including Singleton, Factory, and Observer patterns.

import java.util.*;

// Singleton Pattern
class DatabaseConnection {
    private static DatabaseConnection instance;
    private String connectionString;
    
    private DatabaseConnection() {
        this.connectionString = "jdbc:mysql://localhost:3306/mydb";
    }
    
    public static synchronized DatabaseConnection getInstance() {
        if (instance == null) {
            instance = new DatabaseConnection();
        }
        return instance;
    }
    
    public void connect() {
        System.out.println("Connecting to: " + connectionString);
    }
}

// Factory Pattern
interface Animal {
    void makeSound();
}

class Dog implements Animal {
    public void makeSound() { System.out.println("Woof!"); }
}

class Cat implements Animal {
    public void makeSound() { System.out.println("Meow!"); }
}

class AnimalFactory {
    public static Animal createAnimal(String type) {
        switch (type.toLowerCase()) {
            case "dog": return new Dog();
            case "cat": return new Cat();
            default: throw new IllegalArgumentException("Unknown animal type");
        }
    }
}

// Observer Pattern
interface Observer {
    void update(String message);
}

class Subject {
    private List<Observer> observers = new ArrayList<>();
    
    public void attach(Observer observer) {
        observers.add(observer);
    }
    
    public void notifyObservers(String message) {
        for (Observer observer : observers) {
            observer.update(message);
        }
    }
}

class ConcreteObserver implements Observer {
    private String name;
    
    public ConcreteObserver(String name) {
        this.name = name;
    }
    
    @Override
    public void update(String message) {
        System.out.println(name + " received: " + message);
    }
}

public class DesignPatternsDemo {
    public static void main(String[] args) {
        // Singleton
        DatabaseConnection db1 = DatabaseConnection.getInstance();
        DatabaseConnection db2 = DatabaseConnection.getInstance();
        System.out.println("Same instance: " + (db1 == db2));
        
        // Factory
        Animal dog = AnimalFactory.createAnimal("dog");
        Animal cat = AnimalFactory.createAnimal("cat");
        dog.makeSound();
        cat.makeSound();
        
        // Observer
        Subject subject = new Subject();
        subject.attach(new ConcreteObserver("Observer 1"));
        subject.attach(new ConcreteObserver("Observer 2"));
        subject.notifyObservers("Hello Observers!");
    }
}

Code Editor

Output