Posted on: January 19, 2025 Posted by: rahulgite Comments: 0

Behavioral design patterns focus on communication and interaction between objects. They define how objects collaborate and delegate responsibilities, making systems more flexible and easier to maintain.


Key Behavioral Design Patterns with Examples

1. Chain of Responsibility

Passes requests along a chain of handlers until one of them handles it.

Steps to Implement

  1. Define a handler interface with a method to process requests.
  2. Implement concrete handlers that decide whether to handle the request or pass it to the next handler.
  3. Create a chain by linking handlers together.

Java Example

interface Handler {
    void setNext(Handler next);
    void handleRequest(String request);
}

class ConcreteHandlerA implements Handler {
    private Handler next;

    public void setNext(Handler next) {
        this.next = next;
    }

    public void handleRequest(String request) {
        if (request.equals("A")) {
            System.out.println("Handler A processed the request.");
        } else if (next != null) {
            next.handleRequest(request);
        }
    }
}

Spring Example

  • Spring Security Filter Chain: Filters process security checks in a chain.

Real-World Use Case

  • Support Ticket System: Escalating tickets to higher levels based on criteria.

Advantages

  • Decouples senders and receivers of requests.
  • Simplifies adding or changing handlers.

Disadvantages

  • Requests may go unhandled if no handler is designed for it.

2. Command

Encapsulates a request as an object, allowing you to parameterize clients with different requests, queue or log requests, and support undoable operations.”

Steps to Implement

  1. Define a command interface with an execute method.
  2. Create concrete command classes to encapsulate specific requests.
  3. Use an invoker to execute commands.

💡 Real-World Analogy:

Think of a restaurant:

  • You (the client) place an order (command).
  • The waiter takes the order and hands it to the kitchen (invoker).
  • The chef (receiver) executes the order.

You’re not involved in how the food is prepared — the order (command) carries all that information.

Java Example

interface Command {
    void execute();
}

class Light {
    public void turnOn() {
        System.out.println("Light is ON");
    }

    public void turnOff() {
        System.out.println("Light is OFF");
    }
}

class TurnOnLightCommand implements Command {
    private Light light;

    public TurnOnLightCommand(Light light) {
        this.light = light;
    }

    public void execute() {
        light.turnOn();
    }
}

Spring Example

  • TaskExecutor: Encapsulates tasks to be executed asynchronously.

Real-World Use Case

  • Undo Operations: Storing commands for reversible operations.

Advantages

  • Decouples the sender and receiver.
  • Enables undo/redo functionality.

Disadvantages

  • Can introduce unnecessary complexity.

3. Interpreter

Defines a representation for a grammar and provides an interpreter to deal with this grammar.” It’s typically used for parsing or evaluating expressions or languages.

Steps to Implement

  1. Define an expression interface with an interpret method.
  2. Create concrete classes to represent grammar rules.
  3. Use a context to store information for interpretation.

Real-World Analogy:

Think of a calculator that can evaluate a math expression like 5 + 3 - 2.
Each number and operator follows rules (grammar), and the calculator interprets and evaluates the whole sentence using those rules.

Java Example

interface Expression {
    boolean interpret(String context);
}

class TerminalExpression implements Expression {
    private String data;

    public TerminalExpression(String data) {
        this.data = data;
    }

    public boolean interpret(String context) {
        return context.contains(data);
    }
}

Real-World Use Case

  • SQL Parsers: Interpreting and executing SQL queries.

Advantages

  • Easy to add new interpretations.
  • Provides a formal way to analyze text.

Disadvantages

  • Can become complex for large grammars.

4. Iterator

Provides a way to access elements sequentially without exposing their underlying structure.

Steps to Implement

  1. Define an iterator interface with methods like hasNext and next.
  2. Create concrete iterator classes to traverse specific collections.
  3. Create aggregate classes that provide iterators.

Java Example

interface Iterator {
    boolean hasNext();
    Object next();
}

class NameIterator implements Iterator {
    private String[] names;
    private int index;

    public NameIterator(String[] names) {
        this.names = names;
    }

    public boolean hasNext() {
        return index < names.length;
    }

    public Object next() {
        if (this.hasNext()) {
            return names[index++];
        }
        return null;
    }
}

Spring Example

  • Java Collections Framework: Iterators for lists, sets, etc.

Real-World Use Case

  • Menu Navigation: Iterating through menu items.

Advantages

  • Simplifies traversal logic.
  • Provides uniform access to collections.

Disadvantages

  • May expose the internal structure indirectly.

5. Mediator

Reduces dependencies by centralizing communication between objects.

Steps to Implement

  1. Define a mediator interface to coordinate communication.
  2. Implement concrete mediators to handle interactions.
  3. Create components that communicate via the mediator.

Java Example

interface Mediator {
    void sendMessage(String message, Colleague colleague);
}

class ChatMediator implements Mediator {
    private List<Colleague> colleagues = new ArrayList<>();

    public void addColleague(Colleague colleague) {
        colleagues.add(colleague);
    }

    public void sendMessage(String message, Colleague sender) {
        for (Colleague colleague : colleagues) {
            if (colleague != sender) {
                colleague.receiveMessage(message);
            }
        }
    }
}

Spring Example

  • ApplicationContext: Acts as a mediator for beans to interact with one another.

Real-World Use Case

  • Chat Applications: Managing communication between users.

Advantages

  • Reduces coupling between objects.
  • Simplifies communication logic.

Disadvantages

  • May centralize too much logic into the mediator.

6. Memento

Captures and restores an object’s state without violating encapsulation.

Steps to Implement

  1. Create a Memento class to store the state.
  2. Add methods in the originator to save and restore state.
  3. Use a caretaker to manage memento objects.

Java Example

class Memento {
    private String state;

    public Memento(String state) {
        this.state = state;
    }

    public String getState() {
        return state;
    }
}

class Originator {
    private String state;

    public void setState(String state) {
        this.state = state;
    }

    public Memento saveStateToMemento() {
        return new Memento(state);
    }

    public void getStateFromMemento(Memento memento) {
        state = memento.getState();
    }
}

Spring Example

  • Not commonly seen in Spring but useful for saving states in custom services.

Real-World Use Case

  • Text Editors: Undo/redo functionality.

Advantages

  • Preserves encapsulation of state.
  • Simplifies undo operations.

Disadvantages

  • May consume significant memory for large states.

7. Observer

Sets up a one-to-many dependency so that when one object changes state, others are notified.

Steps to Implement

  1. Define a subject interface to manage observers.
  2. Implement concrete subjects that notify observers.
  3. Create observer interfaces and concrete implementations.

Java Example

interface Observer {
    void update(String message);
}

class Subject {
    private List<Observer> observers = new ArrayList<>();

    public void addObserver(Observer observer) {
        observers.add(observer);
    }

    public void notifyObservers(String message) {
        for (Observer observer : observers) {
            observer.update(message);
        }
    }
}

Spring Example

  • ApplicationEventPublisher: Used to publish and listen to events.

Real-World Use Case

  • Notification Systems: Email or push notifications when an event occurs.

Advantages

  • Promotes loose coupling between subject and observers.
  • Dynamically allows adding and removing observers.

Disadvantages

  • May lead to performance issues with too many observers.
  • Difficult to debug due to multiple dependencies.

8. State

Alters an object’s behavior when its internal state changes.

Steps to Implement

  1. Create a State interface with a method defining state-specific behavior.
  2. Implement concrete states that modify behavior.
  3. Use a context class to manage states.

Java Example

interface State {
    void doAction(Context context);
}

class StartState implements State {
    public void doAction(Context context) {
        System.out.println("State changed to START.");
        context.setState(this);
    }
}

class StopState implements State {
    public void doAction(Context context) {
        System.out.println("State changed to STOP.");
        context.setState(this);
    }
}

class Context {
    private State state;

    public void setState(State state) {
        this.state = state;
    }

    public State getState() {
        return state;
    }
}

Spring Example

  • Spring State Machine: Handles transitions between states in workflows.

Real-World Use Case

  • Vending Machines: Changing behavior based on state (e.g., idle, waiting for input, dispensing).

Advantages

  • Promotes single responsibility by isolating state-specific behavior.
  • Makes state transitions explicit.

Disadvantages

  • Can introduce overhead with many state classes.

9. Strategy

Encapsulates interchangeable algorithms and selects one at runtime.

Steps to Implement

  1. Define a Strategy interface for algorithms.
  2. Implement concrete strategies.
  3. Use a context class to manage strategy selection.

Java Example

interface Strategy {
    int doOperation(int num1, int num2);
}

class Addition implements Strategy {
    public int doOperation(int num1, int num2) {
        return num1 + num2;
    }
}

class Subtraction implements Strategy {
    public int doOperation(int num1, int num2) {
        return num1 - num2;
    }
}

class Context {
    private Strategy strategy;

    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    public int executeStrategy(int num1, int num2) {
        return strategy.doOperation(num1, num2);
    }
}

Spring Example

  • Bean Selection: Using @Primary or @Qualifier to select an implementation dynamically.

Real-World Use Case

  • Payment Gateways: Selecting a payment processor dynamically (e.g., PayPal, Stripe).

Advantages

  • Promotes open/closed principle.
  • Avoids conditional statements by delegating to strategies.

Disadvantages

  • Increases the number of classes.

10. Template Method

Defines the skeleton of an algorithm, letting subclasses override steps.

Steps to Implement

  1. Create an abstract class with a template method.
  2. Add concrete methods for steps that shouldn’t change.
  3. Allow subclasses to override abstract methods.

Java Example

abstract class Game {
    abstract void initialize();
    abstract void startPlay();
    abstract void endPlay();

    public final void play() {
        initialize();
        startPlay();
        endPlay();
    }
}

class Cricket extends Game {
    void initialize() {
        System.out.println("Cricket Game Initialized.");
    }

    void startPlay() {
        System.out.println("Cricket Game Started.");
    }

    void endPlay() {
        System.out.println("Cricket Game Finished.");
    }
}

Spring Example

  • JdbcTemplate: Abstracts repetitive JDBC operations while allowing customization.

Real-World Use Case

  • Data Processing Pipelines: Processing steps like validation, transformation, and saving.

Advantages

  • Enforces consistent behavior in subclasses.
  • Promotes code reuse for invariant steps.

Disadvantages

  • Can make the base class rigid and hard to maintain.

11. Visitor

Encapsulates operations to be performed on elements of an object structure.

Steps to Implement

  1. Define a Visitor interface with visit methods for each element type.
  2. Implement concrete visitors.
  3. Define an Element interface with an accept method.
  4. Implement concrete elements.

Java Example

interface Visitor {
    void visit(Element element);
}

class ConcreteVisitor implements Visitor {
    public void visit(Element element) {
        System.out.println("Visiting: " + element.getName());
    }
}

interface Element {
    void accept(Visitor visitor);
    String getName();
}

class ConcreteElement implements Element {
    private String name;

    public ConcreteElement(String name) {
        this.name = name;
    }

    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

    public String getName() {
        return name;
    }
}

Spring Example

  • BeanPostProcessor: Applies operations to Spring beans during initialization.

Real-World Use Case

  • Serialization Frameworks: Converting objects to XML or JSON.

Advantages

  • Adds operations without modifying object structures.
  • Promotes open/closed principle.

Disadvantages

  • Can make code harder to understand with many visitor methods.

12. Null Object

Provides a non-null default behavior.

Steps to Implement

  1. Define a common interface or abstract class.
  2. Implement a concrete class for the null object with default behavior.
  3. Use the null object in place of null checks.

Java Example

class NullCustomer extends Customer {
    public String getName() {
        return "Not Available";
    }
}

class Customer {
    public String getName() {
        return "Real Customer";
    }
}

Real-World Use Case

  • Default Logger: Logging no-op implementation when no logger is configured.

Advantages

  • Reduces null checks.
  • Simplifies code.

Disadvantages

  • Can hide potential errors by masking null references.

13. Type Object

Separates type from instance to allow flexible object creation.

Steps to Implement

  1. Define a class to represent types.
  2. Create instances based on these types dynamically.

Java Example

class AnimalType {
    private String typeName;

    public AnimalType(String typeName) {
        this.typeName = typeName;
    }
}

class Animal {
    private AnimalType type;

    public Animal(AnimalType type) {
        this.type = type;
    }
}

Real-World Use Case

  • Game Development: Representing character classes or enemy types.

Advantages

  • Promotes flexibility in object creation.
  • Decouples type from implementation.

Disadvantages

  • Increases complexity with additional type classes.

14. Monostate

Shares state across all instances of a class.

Steps to Implement

  1. Use static fields to store shared state.
  2. Provide instance methods to access shared state.

Java Example

class Monostate {
    private static String state;

    public void setState(String s) {
        state = s;
    }

    public String getState() {
        return state;
    }
}

Real-World Use Case

  • Configuration Settings: Sharing settings across multiple instances.

Advantages

  • Combines the simplicity of instance methods with shared state.

Disadvantages

  • May lead to unexpected behavior in multithreaded environments.

Leave a Comment