Core Behavioral Design Patterns in Software Engineering

Chain of Responsibility

Definition: Creates a chain of receiver objects for a request. This pattern decouples the sender of a request from its receivers.

Use Cases: When more than one object may handle a request, and the handler is determined at runtime.

Pros:

  • Reduces coupling between the sender and the receivers.
  • Allows dynamic composition of the responsibility chain.

Cons:

  • Can degrade performance if the chain is too long.
  • Not guaranteed that the request will be handled.
abstract class Approver {
    protected Approver successor;

    public void setNext(Approver successor) {
        this.successor = successor;
    }

    public abstract void process(Task task);
}

class Task {
    private boolean designDone;
    private boolean codingDone;
    private boolean testingDone;
    // Constructors and getters omitted
}

class DesignApprover extends Approver {
    public void process(Task task) {
        if (task.isDesignDone()) {
            System.out.println("Design approved.");
            if (successor != null) successor.process(task);
        } else {
            System.out.println("Design missing. Process stopped.");
        }
    }
}

// Client code
DesignApprover design = new DesignApprover();
CodeApprover code = new CodeApprover();
design.setNext(code);
Task currentTask = new Task(true, false, true);
design.process(currentTask);

Command Pattern

Definition: Encapsulates a request as an object, thereby letting you parameterize clients with different requests, queue requests, or log them.

Use Cases: Need to decouple the object that invokes the operation from the one that knows how to perform it.

Pros:

  • Decouples invoker and receiver.
  • Easy to extend with new commands.

Cons:

  • Increases the number of classes in the system.
interface Action {
    void perform();
}

class Robot {
    public void move() { System.out.println("Robot moving"); }
    public void stop() { System.out.println("Robot stopping"); }
}

class MoveAction implements Action {
    private Robot robot;
    public MoveAction(Robot r) { this.robot = r; }
    public void perform() { robot.move(); }
}

class Controller {
    private List<Action> queue = new ArrayList<>();
    public void store(Action a) { queue.add(a); }
    public void runAll() {
        for (Action a : queue) a.perform();
        queue.clear();
    }
}

// Client code
Controller controller = new Controller();
Robot bot = new Robot();
controller.store(new MoveAction(bot));
controller.store(new StopAction(bot));
controller.runAll();

Interpreter Pattern

Definition: Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language.

Use Cases: Specific grammar or expression evaluation (e.g., SQL parsing, regular expressions).

Pros:

  • Easy to change and extend the grammar.

Cons:

  • Complex grammars are hard to maintain.
interface BooleanExpression {
    boolean evaluate(String input);
}

class Constant implements BooleanExpression {
    private String value;
    public Constant(String v) { this.value = v; }
    public boolean evaluate(String input) { return input.contains(value); }
}

class AndOperator implements BooleanExpression {
    private BooleanExpression left, right;
    public AndOperator(BooleanExpression l, BooleanExpression r) { left = l; right = r; }
    public boolean evaluate(String input) { return left.evaluate(input) && right.evaluate(input); }
}

// Client code
BooleanExpression x = new Constant("foo");
BooleanExpression y = new Constant("bar");
BooleanExpression check = new AndOperator(x, y);
System.out.println(check.evaluate("foobar")); // true

Iterator Pattern

Definition: Provides a way to access the elements of a aggregate object sequentially without exposing its underlying representation.

Use Cases: Traversing collections without knowing the internal structure.

Pros:

  • Simplifies the collection interface.

Cons:

  • Increases the number of classes.

Mediator Pattern

Definition: Defines an object that encapsulates how a set of objects interact, promoting loose coupling by keeping objects from referring to each other explicitly.

Use Cases: When communication between components is complex and tangled.

Pros:

  • Reduces dependencies between classes.

Cons:

  • The Mediator can become a "God Object".
class ChatRoom {
    public static void showMessage(User user, String msg) {
        System.out.println(user.getName() + ": " + msg);
    }
}

class User {
    private String name;
    public void send(String msg) { ChatRoom.showMessage(this, msg); }
}

Memento Pattern

Definition: Without violating encapsulation, capture and externalize an object's internal state so that the object can be restored to this state later.

Use Cases: Undo/Redo functionality.

Pros:

  • Preserves encapsulation boundaries.

Cons:

  • Can be expensive if state is large.
class Editor {
    private String text;
    public EditorSnapshot save() { return new EditorSnapshot(text); }
    public void restore(EditorSnapshot s) { this.text = s.getText(); }
}

class EditorSnapshot {
    private final String text;
    public EditorSnapshot(String t) { text = t; }
    public String getText() { return text; }
}

class Caretaker {
    private Stack<EditorSnapshot> history = new Stack<>();
    public void save(EditorSnapshot s) { history.push(s); }
    public EditorSnapshot pop() { return history.pop(); }
}

Observer Pattern

Definition: Defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

Use Cases: Evant handling systems, distributed event processing.

Pros:

  • Loose coupling between Subject and Observers.
  • Supports broadcast communication.

Cons:

  • Unexpected updates if not managed carefully.
class Subject {
    private List<Subscriber> subs = new ArrayList<>();
    private String state;
    public void attach(Subscriber s) { subs.add(s); }
    public void setState(String s) {
        this.state = s;
        notifyAll();
    }
    private void notifyAll() {
        for (Subscriber s : subs) s.update(state);
    }
}

interface Subscriber { void update(String data); }

class EmailSubscriber implements Subscriber {
    public void update(String data) { System.out.println("Email received: " + data); }
}

State Pattern

Definition: Allows an object to alter its behavior when its internal state changes. The object will appear to change its class.

Use Cases: When an object's behavior depends on its state, and it must change its behavior at runtime.

Pros:

  • Localizes state-specific behavior.
  • Makes state transitions explicit.

Cons:

  • Increases the number of clases.
class MediaPlayer {
    private PlayerState state;
    public void setState(PlayerState s) { state = s; }
    public void clickPlay() { state.pressPlay(this); }
}

interface PlayerState { void pressPlay(MediaPlayer context); }

class ReadyState implements PlayerState {
    public void pressPlay(MediaPlayer c) {
        System.out.println("Playing...");
        c.setState(new PlayingState());
    }
}

Strategy Pattern

Definition: Defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it.

Use Cases: When you have multiple ways to perform a specific task.

Pros:

  • Avoids conditional statements.
  • Provides flexibility.

Cons:

  • Client must be aware of different strategies.

Template Method Pattern

Definition: Defines the skeleton of an algorithm in a method, deferring some steps to subclasses. The Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.

Use Cases: To avoid code duplication in similar processes.

Pros:

  • Code reuse.
  • Inversion of control (Hollywood Principle).

Cons:

  • Inheritance restrictions.
abstract class DataProcessor {
    public final void process() {
        read();
        if (shouldParse()) parse();
        write();
    }
    abstract void read();
    abstract void write();
    void parse() {}
    boolean shouldParse() { return false; }
}

class CsvProcessor extends DataProcessor {
    void read() { System.out.println("Reading CSV"); }
    void write() { System.out.println("Writing CSV"); }
    boolean shouldParse() { return true; }
}

Visitor Pattern

Definition: Represents an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.

Use Cases: When you need to perform operations across a set of objects with different interfaces.

Pros:

  • Easy to add new operations.

Cons:

  • Hard to add new element types.
interface Element { void accept(Visitor v); }

class Book implements Element {
    private double price;
    public void accept(Visitor v) { v.visit(this); }
}

interface Visitor { void visit(Book b); }

class PriceCalculator implements Visitor {
    public void visit(Book b) {
        System.out.println("Book price: " + b.getPrice());
    }
}

Tags: Design Patterns Behavioral Patterns java Software Architecture Object-Oriented Programming

Posted on Thu, 27 Aug 2026 16:20:13 +0000 by offnordberg