0% found this document useful (0 votes)
5 views

Behavioral Design Patterns

Behavioral design patterns focus on improving communication and interaction between objects, enhancing system flexibility and dynamics. Key patterns include Chain of Responsibility, Command, Interpreter, Iterator, Mediator, Observer, State, and Strategy, each serving specific use cases in software design. These patterns help decouple components, manage object behavior, and streamline request handling in various applications.
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
5 views

Behavioral Design Patterns

Behavioral design patterns focus on improving communication and interaction between objects, enhancing system flexibility and dynamics. Key patterns include Chain of Responsibility, Command, Interpreter, Iterator, Mediator, Observer, State, and Strategy, each serving specific use cases in software design. These patterns help decouple components, manage object behavior, and streamline request handling in various applications.
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 33

Behavioral Design Patterns

 are a category of design patterns that focus on improving or streamlining the


communication and interaction between objects
 are concerned with the behavior of objects and how they cooperate with one another
 can make a system more flexible and dynamic by defining clear communication
between objects, handling algorithmic flow, and ensuring that object relationships are
maintained without tightly coupling them

Key Behavioral Design Patterns in Java:


1. Chain of Responsibility Pattern:
o Allows passing a request along a chain of handlers. Each handler processes the
request or passes it to the next handler in the chain.
o Use Case: When multiple handlers are available, and one of them may be able to
process a request.
Example: Logging systems, event handling in UI frameworks.
2. Command Pattern:
o Encapsulates a request as an object, thereby allowing for parameterization of
clients with queues, requests, and operations.
o Use Case: Undo/redo functionality, task scheduling, or queuing requests for
execution.
Example: Menu buttons in GUI applications (each button could be a command).
3. Interpreter Pattern:
o Defines a grammar for a language and provides an interpreter to evaluate
sentences in the language.
o Use Case: Used in designing compilers, parsers, and interpreters.
Example: SQL query interpreters.
4. Iterator Pattern:
o Provides a way to access the elements of an aggregate object sequentially
without exposing its underlying representation (structure).
o Use Case: Accessing collections like lists, sets, and maps in a consistent
manner.
Example: Iterating over elements in a list or a set in Java.
5. Mediator Pattern:
o Defines an object that controls the interaction between a set of objects,
reducing the complexity of communication between objects.
o Use Case: When a group of objects need to communicate, but direct
communication would lead to a tight coupling between them.
Example: Chat applications where a central server mediates communication
between clients.
6. Observer Pattern:
o Defines a one-to-many dependency relationship where a change in one object
(subject) automatically notifies and updates its dependents (observers).
o Use Case: Event handling systems, like GUI event listeners or notification
systems.
Example: A stock price update system where multiple clients (observers) are
notified when the price changes.
7. State Pattern:
o Allows an object to alter its behavior when its internal state changes, as if the
object changed its class.
o Use Case: Systems with a finite number of states, such as a vending machine, or
a game with different levels.
Example: Traffic light simulation, where the state (green, yellow, red) controls the
behavior of the traffic light.
8. Strategy Pattern:
o Defines a family of algorithms and allows them to be interchangeable. It lets the
algorithm vary independently from the clients that use it.
o Use Case: When you have multiple ways of performing a task and want to select
the behavior at runtime.
Example: Sorting algorithms where different strategies (like quick sort, merge
sort) can be chosen based on input size.

CHAIN OF RESPONSIBILITY PATTERN


The Chain of Responsibility is a behavioral design pattern that allows passing a
request along a chain of handlers. Each handler in the chain processes the request or passes
it to the next handler in the chain. This pattern decouples the sender of a request from its
receivers, allowing multiple objects to handle the request independently.

Intent of the Chain of Responsibility Pattern:


 The Chain of Responsibility Pattern allows a request to be passed along a chain of
objects until one of them handles the request.
 The handler objects in the chain can either process the request or pass it along to the
next handler in the chain.
 This pattern helps achieve decoupling between the sender (client) and the receivers
(handlers), as the sender doesn't need to know which handler will handle the request.

Key Components of the Chain of Responsibility Pattern:


1. Handler (Abstract Handler):
o Defines an interface for handling requests and maintaining a reference to the
next handler in the chain.
2. ConcreteHandler:
o Implements the handling logic for the request. If the concrete handler can
handle the request, it processes it; otherwise, it forwards the request to the
next handler in the chain.
3. Client:
o Initiates the request and passes it to the first handler in the chain. The request
will be passed along the chain until it's processed.
How It Works:
1. The client sends the request to the first handler in the chain.
2. The handler either handles the request or passes it to the next handler in the chain.
3. This process continues until the request is handled or the end of the chain is reached
(if no handler is capable of handling it).

Benefits of the Chain of Responsibility Pattern:


 Decouples Sender and Receiver: The sender doesn't need to know which handler will
process the request.
 Dynamic Request Handling: Handlers can be added or removed at runtime, allowing
dynamic configuration of request processing chains.
 Single Responsibility: Each handler focuses on a specific task, promoting separation of
concerns.

Drawbacks:
 Performance: If there are many handlers, it can lead to performance overhead as the
request may need to be passed through many handlers.
 Unpredictability: It can sometimes be difficult to predict which handler will process
the request, especially in large chains.
Example of Chain of Responsibility Pattern in Java:
In this example, let's say we have a system that processes different levels of support tickets.
Each handler in the chain represents a different level of support (e.g., JuniorSupport,
SeniorSupport, ManagerSupport), and each will handle requests according to their level.
Java Code Example:
// Handler Interface
interface SupportHandler {
void handleRequest(String request);
void setNext(SupportHandler nextHandler); // Set the next handler in the chain
}

// Concrete Handler 1: Junior Support


class JuniorSupport implements SupportHandler {
private SupportHandler nextHandler;

@Override
public void handleRequest(String request) {
if (request.equals("Basic")) {
System.out.println("JuniorSupport: Handling basic request");
} else if (nextHandler != null) {
nextHandler.handleRequest(request); // Pass the request to the next handler
}
}

@Override
public void setNext(SupportHandler nextHandler) {
this.nextHandler = nextHandler;
}
}

// Concrete Handler 2: Senior Support


class SeniorSupport implements SupportHandler {
private SupportHandler nextHandler;

@Override
public void handleRequest(String request) {
if (request.equals("Advanced")) {
System.out.println("SeniorSupport: Handling advanced request");
} else if (nextHandler != null) {
nextHandler.handleRequest(request); // Pass the request to the next handler
}
}

@Override
public void setNext(SupportHandler nextHandler) {
this.nextHandler = nextHandler;
}
}

// Concrete Handler 3: Manager Support


class ManagerSupport implements SupportHandler {
private SupportHandler nextHandler;

@Override
public void handleRequest(String request) {
if (request.equals("Critical")) {
System.out.println("ManagerSupport: Handling critical request");
} else {
System.out.println("ManagerSupport: Unable to handle request");
}
}

@Override
public void setNext(SupportHandler nextHandler) {
this.nextHandler = nextHandler;
}
}

// Client Code
public class ChainOfResponsibilityExample {
public static void main(String[] args) {
// Create the handlers (support levels)
SupportHandler juniorSupport = new JuniorSupport();
SupportHandler seniorSupport = new SeniorSupport();
SupportHandler managerSupport = new ManagerSupport();

// Chain the handlers


juniorSupport.setNext(seniorSupport);
seniorSupport.setNext(managerSupport);

// Client makes a request


System.out.println("Client: Making a Basic request...");
juniorSupport.handleRequest("Basic");

System.out.println("\nClient: Making an Advanced request...");


juniorSupport.handleRequest("Advanced");

System.out.println("\nClient: Making a Critical request...");


juniorSupport.handleRequest("Critical");

System.out.println("\nClient: Making an Unknown request...");


juniorSupport.handleRequest("Unknown");
}
}
Explanation of Code:

1. SupportHandler: The SupportHandler interface defines the contract for handling


requests. It has a method handleRequest(String request) to process requests and a
method setNext(SupportHandler nextHandler) to set the next handler in the chain.

2. Concrete Handlers: JuniorSupport, SeniorSupport, and ManagerSupport are concrete


implementations of the SupportHandler interface. Each handler can process specific
types of requests (e.g., "Basic", "Advanced", "Critical"). If it cannot handle the request,
it passes the request to the next handler in the chain.

3. Client: In the main method, the client creates the chain of handlers and makes various
requests. Each handler in the chain will process the request according to its ability or
pass it to the next handler.

When to Use Chain of Responsibility Pattern:

When multiple objects can handle a request: If you have several objects that can
handle a particular request, but you don't know in advance which one will handle it.

When you want to decouple the sender and receiver: The sender doesn't need to know
which handler will process the request, making the system more flexible.

When the responsibility of handling requests can be passed dynamically: For example,
different levels of a customer service team handling different types of support

requests.

Real-World Examples:

Event handling in GUI frameworks: Events like mouse clicks or key presses can be
passed through a chain of event listeners.

Logging systems: Loggers can be organized in a chain where each logger handles
different levels of log messages (e.g., DEBUG, INFO, ERROR).

Validation systems: Multiple validation rules can be chained together, with each rule
handling different parts of the validation process.

COMMAND PATTERN DESIGN
The Command Pattern is a behavioral design pattern that encapsulates a request as an
object, thereby allowing users to parameterize clients with queues, requests, and operations.
It decouples the sender of a request from its receiver, allowing for more flexibility in
handling requests. This pattern is particularly useful for undo/redo functionality, task
scheduling, and request queuing.

Intent of the Command Pattern:


The command pattern allows you to:
 Encapsulate a request as an object, which includes all necessary information to
perform the action.
 Separate the object that invokes the operation from the object that knows how to
perform the operation.
 Parameterize clients with different requests, queue requests, and log the requests.
 Support undo/redo operations by keeping track of the operations performed.

Key Components of the Command Pattern:


1. Command (Abstract Command):
o This is the interface or abstract class that declares the execute() method, which
is used to invoke the operation.
2. ConcreteCommand:
o This class implements the execute() method and binds the action (that will be
performed) with the receiver.
3. Receiver:
o This class contains the real action or business logic to be executed. The
command object calls the receiver to perform the actual work.
4. Invoker:
o The invoker is responsible for asking the command to execute the action. It
doesn't know what the action actually is—it only knows that it can call the
execute() method on a command object.
5. Client:
o The client creates a concrete command object and sets its receiver. The client
then passes the command object to the invoker.
How It Works:
1. The client creates a command object and associates it with a specific receiver (an
object that will perform the actual action).
2. The invoker stores the command object and asks it to execute.
3. The command object executes the request by calling the appropriate method on the
receiver.
Benefits of the Command Pattern:
 Loose Coupling: The sender doesn't need to know the details of the receiver or how
the request is executed.
 Undo/Redo Operations: Command objects can store previous states for undo or redo
functionality.
 Extensibility: New commands can be added without changing the invoker or client.
 Queue Requests: Requests can be queued, delayed, or logged.
Drawbacks:
 Complexity: Adding many command objects can increase the number of classes and
complexity in the codebase.
 Performance: Since each request is encapsulated in a separate object, there might be
overhead if there are a large number of requests.

Example of Command Pattern in Java:


Let’s implement the Command Pattern in a simple remote control system, where each button
on the remote control will be associated with a different command.
Java Example Code:
// Command Interface
interface Command {
void execute();
}

// Receiver: The device that performs the action


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

public void turnOff() {


System.out.println("The light is OFF");
}
}

// Concrete Command: Turn on the light


class LightOnCommand implements Command {
private Light light;

public LightOnCommand(Light light) {


this.light = light;
}

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

// Concrete Command: Turn off the light


class LightOffCommand implements Command {
private Light light;

public LightOffCommand(Light light) {


this.light = light;
}

@Override
public void execute() {
light.turnOff();
}
}

// Invoker: The remote control


class RemoteControl {
private Command command;

public void setCommand(Command command) {


this.command = command;
}

public void pressButton() {


command.execute();
}
}

// Client code
public class CommandPatternExample {
public static void main(String[] args) {
// Receiver: The light
Light livingRoomLight = new Light();

// Concrete Commands: Create the on/off commands


Command lightOn = new LightOnCommand(livingRoomLight);
Command lightOff = new LightOffCommand(livingRoomLight);

// Invoker: The remote control


RemoteControl remote = new RemoteControl();

// Turn the light ON using the remote


remote.setCommand(lightOn);
remote.pressButton();

// Turn the light OFF using the remote


remote.setCommand(lightOff);
remote.pressButton();
}
Explanation of the Code:
1. Command Interface:
o The Command interface defines an abstract execute() method, which concrete
command classes will implement.
2. Concrete Commands:
o LightOnCommand and LightOffCommand are concrete implementations of the
Command interface. Each of these classes encapsulates a request to turn the
light on or off, respectively.
3. Receiver:
o The Light class is the receiver. It has methods turnOn() and turnOff(), which
perform the actual action of turning the light on and off.
4. Invoker:
o The RemoteControl class acts as the invoker. It holds a reference to a Command
object and calls its execute() method when the button on the remote is pressed.
5. Client:
o The client creates the Light object (receiver), the concrete command objects
(LightOnCommand and LightOffCommand), and associates the commands with
the invoker (RemoteControl). The client then invokes the pressButton() method
to execute the command.

Use Cases for Command Pattern:


 Undo/Redo functionality: In applications like text editors, where actions can be
undone or redone.
 Queuing Requests: In task scheduling or queuing systems, where commands can be
added to a queue and processed later.
 Menu Systems: In GUI applications, where each button can represent a command
(e.g., Open, Save, Exit).
 Logging: In logging systems, where commands can be logged and later executed.

INTERPRETER PATTERN DESIGN


The Interpreter Pattern is a behavioral design pattern that provides a way to evaluate
language grammar or expressions. It defines a representation for the grammar of a language
and provides an interpreter for interpreting sentences in the language. This pattern is
particularly useful when designing an interpreter for a specific language or when dealing
with problems that require parsing and interpreting text or expressions, such as in compilers
or expression evaluators.

Intent of the Interpreter Pattern:


 To define a grammatical representation for a language.
 To provide an interpreter that interprets sentences or expressions in that language.
 The Interpreter Pattern is useful for designing systems that evaluate expressions, such
as arithmetic expressions, logical conditions, or parsing language constructs.

Key Components of the Interpreter Pattern:


1. AbstractExpression:
o This is an abstract class or interface that declares an interpret() method, which
is implemented by concrete expressions.
2. TerminalExpression:
This class implements the interpret() method for the base elements of the
language. Terminal expressions are the basic building blocks of the language
o

that cannot be broken down further.


3. NonTerminalExpression:
o This class implements the interpret() method for non-terminal elements, which
combine other expressions (either terminal or non-terminal) to form a more
complex expression.
4. Context:
o This contains information that is global to the interpretation, like variables or
other states required by the interpreters.
5. Client:
o The client initializes the abstract syntax tree of the language and invokes the
interpret() method to interpret the expression.
How It Works:
1. The client creates a representation of the language expression by assembling terminal
and non-terminal expressions.
2. The interpreter evaluates the expression by recursively interpreting each element.
3. The result of the interpretation is returned to the client.
Benefits of the Interpreter Pattern:
 Clear structure: It provides a way to define a language’s grammar and the method to
interpret it.
 Extensibility: New expressions can be added easily without changing the existing
code.
 Flexibility: It supports recursion and complex structures in interpreting grammar.
Drawbacks:
 Complexity: It may lead to excessive class creation if the grammar is complex or has
many rules.
 Performance: If not implemented efficiently, interpreting large or complex expressions
can become slow due to the recursive nature of the pattern.

Example of Interpreter Pattern in Java:

Let’s implement an example where we interpret simple arithmetic expressions involving


addition and subtraction.

Java Code Example:


// Abstract Expression
interface Expression {
int interpret();
}

// Terminal Expressions
class Number implements Expression {
private int number;

public Number(int number) {


this.number = number;
}

@Override
public int interpret() {
return number;
}
}
// Non-Terminal Expressions
class Add implements Expression {
private Expression left;
private Expression right;

public Add(Expression left, Expression right) {


this.left = left;
this.right = right;
}

@Override
public int interpret() {
return left.interpret() + right.interpret();
}
}

class Subtract implements Expression {


private Expression left;
private Expression right;

public Subtract(Expression left, Expression right) {


this.left = left;
this.right = right;
}

@Override
public int interpret() {
return left.interpret() - right.interpret();
}
}

// Client code
public class InterpreterPatternExample {
public static void main(String[] args) {
// Creating the expression "5 + 3 - 2"
Expression five = new Number(5);
Expression three = new Number(3);
Expression two = new Number(2);

// Build the expression tree


Expression addExpression = new Add(five, three); // 5 + 3
Expression subtractExpression = new Subtract(addExpression, two); // (5 + 3) - 2

// Evaluate the expression


System.out.println("Result: " + subtractExpression.interpret()); // Output should be 6
}
}

Explanation of the Code:


1. Expression Interface:
o The Expression interface defines the interpret() method, which will be
implemented by both terminal and non-terminal expressions.
2. Terminal Expression (Number):
o The Number class represents terminal expressions. Each Number object holds
an integer and returns that integer when its interpret() method is called.
3. Non-Terminal Expressions (Add and Subtract):
o The Add and Subtract classes represent non-terminal expressions that perform
operations on other expressions (either terminal or non-terminal). They each
take two Expression objects (left and right) and implement the interpret()
method to evaluate the result of the operation.
4. Client:
o The client creates instances of the terminal and non-terminal expressions,
forming an abstract syntax tree (AST). It then evaluates the expression by
calling the interpret() method on the root of the tree (subtractExpression).

When to Use Interpreter Pattern:


 Expression evaluation: When you need to evaluate mathematical expressions, boolean
expressions, or any type of domain-specific language.
 Compilers: When implementing a compiler or interpreter for a custom language.
 Configuration and rule parsing: When you need to parse configuration files or define
rules for systems (e.g., defining and interpreting rules in a business process).

Real-World Use Cases:


 Mathematical expression evaluation: Calculators or systems that need to interpret
arithmetic or logical expressions.
 SQL Parsing: Interpreting and processing SQL queries.
 Regex: Interpreting regular expressions.
 Business rule engines: Interpreting business rules that are specified in a natural
language or custom grammar.

ITERATOR PATTERN DESIGN


The Iterator Pattern is a behavioral design pattern that provides a way to access
elements of a collection (like a list, set, or array) sequentially without exposing the
underlying structure of the collection. It provides a standardized way to iterate over the
elements of a collection, often abstracting away the details of how the collection is
structured.

Intent of the Iterator Pattern:


 To provide a way to sequentially access elements of a collection without exposing the
internal structure of the collection.
 It helps decouple the collection class from the clients that use it, ensuring that the
collection can be modified or extended independently of the client code.
 The iterator pattern allows for multiple traversal mechanisms of a collection without
needing to expose the underlying data structures.

Key Components of the Iterator Pattern:


1. Iterator (Interface):
o This interface defines the methods required for iterating through the collection.
The main methods include:
 hasNext(): Checks if there are more elements to iterate.
 next(): Returns the next element in the collection.
2. ConcreteIterator:
o This class implements the Iterator interface and provides the concrete logic for
iterating over the collection.
3. Aggregate (Collection):
o This is an interface or abstract class that defines the createIterator() method,
which returns an iterator object for traversing the collection.
4. ConcreteAggregate (ConcreteCollection):
o This class implements the Aggregate interface and holds the collection. It
provides the method to create and return a specific iterator for the collection.
How It Works:
1. The client interacts with the Iterator interface and calls the hasNext() and next()
methods to traverse the collection.
2. The Iterator is responsible for keeping track of the position while iterating, so the
client doesn't need to manage this.
3. The ConcreteIterator knows how to navigate through the specific data structure.
4. The Aggregate or ConcreteAggregate provides a method (createIterator()) to return
the iterator.
Benefits of the Iterator Pattern:
 Separation of Concerns: The collection is separate from the iteration logic.
 Flexibility: The same collection can be iterated in different ways (for example, forward
and backward).
 Loose Coupling: The client code doesn't need to know the internal structure of the
collection (e.g., whether it's an array, linked list, etc.).
 Extensibility: You can easily add new ways to iterate over the collection (like a reverse
iterator) without changing the collection's internal structure.

Drawbacks:
 Overhead: If not carefully designed, it might introduce additional complexity or
overhead, especially with large collections.
 Performance: Iteration could become less efficient if not implemented properly for
specific data structures.

Example of Iterator Pattern in Java:


Let’s implement an example of the Iterator Pattern where we have a collection of books, and
we want to iterate over the books in the collection.

Java Code Example:

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

// Aggregate Interface (Collection)


interface BookCollection {
Iterator createIterator();
}

// ConcreteIterator: A specific iterator for BookCollection


class BookIterator implements Iterator {
private Book[] books;
private int position = 0;

public BookIterator(Book[] books) {


this.books = books;
}

@Override
public boolean hasNext() {
return position < books.length && books[position] != null;
}

@Override
public Object next() {
if (this.hasNext()) {
return books[position++];
}
return null;
}
}
// ConcreteAggregate: A specific collection for Books
class BookShelf implements BookCollection {
private Book[] books;
private int numberOfBooks = 0;

public BookShelf(int capacity) {


books = new Book[capacity];
}

public void addBook(Book book) {


if (numberOfBooks < books.length) {
books[numberOfBooks] = book;
numberOfBooks++;
}
}

@Override
public Iterator createIterator() {
return new BookIterator(books);
}
}

// Book Class (Element of the collection)


class Book {
private String title;

public Book(String title) {


this.title = title;
}

public String getTitle() {


return title;
}
}

// Client code
public class IteratorPatternExample {
public static void main(String[] args) {
// Create a collection (BookShelf) and add books to it
BookShelf bookShelf = new BookShelf(5);
bookShelf.addBook(new Book("Design Patterns"));
bookShelf.addBook(new Book("Clean Code"));
bookShelf.addBook(new Book("The Pragmatic Programmer"));

// Create an iterator to traverse the collection


Iterator iterator = bookShelf.createIterator();

// Iterate over the collection


while (iterator.hasNext()) {
Book book = (Book) iterator.next();
System.out.println("Book: " + book.getTitle());
}
}
}

Explanation of the Code:


1. Iterator Interface:
o The Iterator interface defines two essential methods: hasNext() to check if there
are more elements, and next() to retrieve the next element in the collection.
2. ConcreteIterator (BookIterator):
The BookIterator class implements the Iterator interface. It maintains a position
and knows how to traverse the array of Book objects. The next() method
o

retrieves the next book in the array.


3. BookCollection (Aggregate):
o The BookCollection interface defines the createIterator() method to create and
return an iterator for the collection.
4. ConcreteAggregate (BookShelf):
o The BookShelf class is a concrete implementation of the BookCollection
interface. It holds an array of Book objects and provides the createIterator()
method, which returns an iterator to traverse through the collection.
5. Book Class (Element):
o The Book class represents the items in the collection. It stores the title of the
book and provides a method getTitle() to retrieve the title.
6. Client Code:
o The client creates a BookShelf, adds some Book objects, and then uses the
iterator to traverse the collection, printing each book's title.

When to Use Iterator Pattern:


 When you need to traverse a collection: The iterator pattern is ideal when you need to
iterate over a collection (like lists, arrays, or maps) without exposing its internal
structure.
 When different iteration methods are needed: You can create multiple iterator classes
to iterate in different ways (e.g., forward, backward, or skipping elements).
 When collection structure might change: If you want to decouple the iteration logic
from the internal structure of the collection (e.g., array, linked list), the iterator
pattern provides flexibility.

Real-World Use Cases:


 Java Collections Framework: In Java, the Iterator interface is implemented by
collections like ArrayList, HashSet, LinkedList, and others to provide a standardized
way to iterate over elements.
 GUI Frameworks: Iterating through a list of widgets or components in a GUI library.
 Database Systems: Iterating through records or results in a database query result.
 File Systems: Traversing files or directories.

MEDIATOR PATTERN DESIGN


The Mediator Pattern is a behavioral design pattern that defines an object that
controls the interactions between a group of objects. This pattern promotes loose coupling
by ensuring that instead of objects communicating directly with each other, they
communicate through a central mediator object. The mediator handles the communication
and coordination between the objects, which reduces dependencies between them and
simplifies maintenance.

Intent of the Mediator Pattern:


 To centralize the communication between different objects by providing a mediator
that handles all communication.
 To reduce the complexity of interactions between multiple objects by decoupling them,
leading to easier maintenance and flexibility.
 To make the system easier to extend by avoiding direct relationships between
components.

Key Components of the Mediator Pattern:


1. Mediator (Interface):
The mediator interface declares the mediate() method, which is used to
facilitate communication between different colleagues (objects).
o

2. ConcreteMediator:
o The concrete mediator class implements the mediator interface and coordinates
communication between the various colleague objects.
3. Colleague:
o Colleague objects are the participants in the system. They don’t communicate
directly with each other but instead communicate through the mediator.
4. Client:
o The client initializes the mediator and colleagues and triggers the
communication through the mediator.
How It Works:
1. The colleague objects send their requests or messages to the mediator rather than
communicating with each other directly.
2. The mediator receives the request and decides which colleague object needs to be
notified based on the request.
3. The mediator updates the relevant colleagues and facilitates the communication.

Benefits of the Mediator Pattern:


 Reduced complexity: Mediator reduces the number of direct connections between
objects, simplifying the system.
 Centralized control: The mediator centralizes control, making it easier to manage
communication.
 Loose coupling: Colleague objects are decoupled from one another and only depend on
the mediator.
 Extensibility: Adding new colleague objects or modifying interactions is simpler
without modifying the existing objects.

Drawbacks:
 Single Point of Failure: If the mediator becomes too complex, it can become a
bottleneck and a single point of failure.
 Overhead: The pattern introduces another layer of indirection (mediator) which can
increase complexity in certain cases.

Example of Mediator Pattern in Java:


Let’s implement a simple chat system where different users (colleagues) communicate with
each other through a central chat room (mediator).

Java Code Example:


import java.util.ArrayList;
import java.util.List;

// Mediator Interface
interface ChatMediator {
void sendMessage(String message, User user);
void addUser(User user);
}

// ConcreteMediator: ChatRoom
class ChatRoom implements ChatMediator {
private List<User> users;

public ChatRoom() {
this.users = new ArrayList<>();
}

@Override
public void sendMessage(String message, User user) {
for (User u : users) {
// Don't send the message to the user who sent it
if (u != user) {
u.receiveMessage(message);
}
}
}
@Override
public void addUser(User user) {
this.users.add(user);
}
}

// Colleague: User
abstract class User {
protected ChatMediator mediator;
protected String name;

public User(ChatMediator mediator, String name) {


this.mediator = mediator;
this.name = name;
}

public abstract void sendMessage(String message);


public abstract void receiveMessage(String message);
}

// Concrete Colleague: ConcreteUser


class ConcreteUser extends User {

public ConcreteUser(ChatMediator mediator, String name) {


super(mediator, name);
}

@Override
public void sendMessage(String message) {
System.out.println(name + " sends message: " + message);
mediator.sendMessage(message, this);
}

@Override
public void receiveMessage(String message) {
System.out.println(name + " received message: " + message);
}
}

// Client code
public class MediatorPatternExample {
public static void main(String[] args) {
ChatMediator chatRoom = new ChatRoom();

User user1 = new ConcreteUser(chatRoom, "User1");


User user2 = new ConcreteUser(chatRoom, "User2");
User user3 = new ConcreteUser(chatRoom, "User3");

chatRoom.addUser(user1);
chatRoom.addUser(user2);
chatRoom.addUser(user3);

// Users send messages through the mediator


user1.sendMessage("Hello, everyone!");
user2.sendMessage("Hi, User1!");
user3.sendMessage("Good morning!");
}
Explanation of the Code:
1. ChatMediator Interface:
o The ChatMediator interface defines two key methods: sendMessage() (for
sending messages to other users) and addUser() (to add users to the mediator).
2. ChatRoom (ConcreteMediator):
o The ChatRoom class is a concrete implementation of the ChatMediator
interface. It maintains a list of User objects (colleagues) and handles the
message forwarding logic. When a message is sent by a user, the ChatRoom
forwards the message to all other users except the sender.
3. User (Colleague):
o The User class is an abstract class representing the colleagues that
communicate through the mediator. Each user has a sendMessage() method for
sending messages and a receiveMessage() method for receiving messages.
4. ConcreteUser (Concrete Colleague):
o The ConcreteUser class extends User and implements the sendMessage() and
receiveMessage() methods. When a user sends a message, it calls the
sendMessage() method of the mediator (ChatRoom), and the mediator ensures
the message is sent to the other users.
5. Client Code:
o In the client code, we create a ChatRoom object (the mediator) and add multiple
ConcreteUser objects (colleagues) to it. Then, each user sends a message, and
the mediator forwards the message to all other users.

When to Use Mediator Pattern:


 Centralized communication: When you need to manage complex communication
between multiple objects and want to centralize control in a single object (the
mediator).
 Loose coupling: When objects in a system should not depend on each other directly
but should communicate through a mediator to reduce dependencies.
 UI Dialogs: When you have a GUI with many components, and you want to manage
interactions between components (like buttons, text fields, etc.) through a mediator
instead of having components directly communicate with each other.
 Complex workflows: In systems where multiple components need to coordinate in a
controlled manner (like in workflow management systems).

Real-World Use Cases:


 Chat Applications: As shown in the example, chat applications where messages need
to be sent between users (colleagues) through a central chat room (mediator).
 UI Dialogs: In GUI-based applications, where components like buttons, sliders, text
fields, and labels need to communicate with each other but shouldn't be tightly
coupled.
 Complex Event Handling Systems: Systems that have various subsystems that need to
communicate but shouldn't be directly dependent on one another.
 Workflow Systems: Complex workflows in business processes where the
communication between steps in the workflow needs to be managed by a mediator.

OBSERVER PATTERN DESIGN


The Observer Pattern is a behavioral design pattern that defines a one-to-many
dependency between objects, where a change in one object (subject) triggers notifications to
all dependent objects (observers). This pattern is particularly useful when you have a
situation where one object’s state changes and you need to automatically update all other
dependent objects.
The observer pattern is often used in scenarios like event handling systems, real-time
updates, and push notification systems, among others.

Intent of the Observer Pattern:


 To allow an object (the subject) to notify other objects (the observers) automatically
when its state changes.
 To decouple the subject and observers so that the subject doesn't need to know who
its observers are or how many there are, only that they need to be notified when the
subject’s state changes.

Key Components of the Observer Pattern:


1. Subject (Observable):
o The Subject maintains a list of observers and provides methods to add, remove,
and notify them. The subject is usually the object whose state changes and
needs to notify observers.
2. Observer:
o The Observer defines an update method that is called by the subject to notify
the observers about a change. Each observer needs to implement this update
method.
3. ConcreteSubject:
o This is the concrete implementation of the Subject. It stores the state of the
subject and notifies the observers when there is a change in its state.
4. ConcreteObserver:
o This is the concrete implementation of the Observer. It contains the logic to
update the observer’s state when it is notified of a change by the subject.

How It Works:
1. The Subject maintains a list of Observers that have registered interest in changes to
the subject's state.
2. When the state of the Subject changes, it notifies all its Observers by calling their
update() method.
3. Each Observer updates its state based on the new state of the Subject.

Benefits of the Observer Pattern:


 Decoupling: The subject and observers are loosely coupled. The subject doesn’t need
to know anything about the concrete observers.
 Flexibility: It allows you to add or remove observers dynamically.
 Automatic Notification: Observers are automatically notified when the subject’s state
changes, reducing the need for manual checks or updates.

Drawbacks:
 Memory Leaks: If observers are not properly removed from the subject's list, it can
lead to memory leaks.
 Performance Issues: If there are a large number of observers, notifying them can
result in performance issues.

Example of Observer Pattern in Java:


Let’s implement a simple weather monitoring system, where multiple display devices
(observers) display weather data (subject) that changes over time.

Java Code Example:


import java.util.ArrayList;
import java.util.List;

// Observer Interface
interface Observer {
void update(float temperature, float humidity, float pressure);
}

// Subject Interface (Observable)


interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}

// ConcreteSubject: WeatherData
class WeatherData implements Subject {
private List<Observer> observers;
private float temperature;
private float humidity;
private float pressure;
public WeatherData() {
observers = new ArrayList<>();
}

@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}

@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}

@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(temperature, humidity, pressure);
}
}

// Set new weather data and notify observers


public void setMeasurements(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
notifyObservers(); // Notify all observers with the new data
}
}

// ConcreteObserver: CurrentConditionsDisplay
class CurrentConditionsDisplay implements Observer {
private float temperature;
private float humidity;
private Subject weatherData;

public CurrentConditionsDisplay(Subject weatherData) {


this.weatherData = weatherData;
weatherData.registerObserver(this);
}

@Override
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
display();
}

public void display() {


System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "%
humidity.");
}
}

// ConcreteObserver: StatisticsDisplay
class StatisticsDisplay implements Observer {
private float temperature;
private float humidity;
private float pressure;
private Subject weatherData;

public StatisticsDisplay(Subject weatherData) {


this.weatherData = weatherData;
weatherData.registerObserver(this);
}

@Override
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
public void display() {
System.out.println("Statistics: " + temperature + "F degrees, " + humidity + "% humidity, " +
pressure + " pressure.");
}
}

// Client code
public class ObserverPatternExample {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();

// Create observers
CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);
StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);

// Simulate new weather data coming in


weatherData.setMeasurements(80, 65, 30.4f);
weatherData.setMeasurements(82, 70, 29.2f);
weatherData.setMeasurements(78, 90, 29.2f);
}

Explanation of the Code:


1. Observer Interface:
o The Observer interface defines the update() method that will be called to
update the observer with the latest data (in this case, temperature, humidity,
and pressure).
2. Subject Interface:
o The Subject interface defines methods for adding, removing, and notifying
observers. It keeps track of all registered observers.
3. ConcreteSubject (WeatherData):
o The WeatherData class is a concrete implementation of the Subject interface. It
stores the weather data (temperature, humidity, pressure) and notifies all
registered observers whenever the data changes by calling notifyObservers().
4. ConcreteObserver (CurrentConditionsDisplay):
o The CurrentConditionsDisplay class is a concrete implementation of the
Observer interface. It displays the current weather conditions when it is notified
of a change in the weather data.
5. ConcreteObserver (StatisticsDisplay):
o The StatisticsDisplay class is another observer that shows statistics based on
the weather data.
6. Client Code:
o The client creates a WeatherData object and two observers
(CurrentConditionsDisplay and StatisticsDisplay). When the weather data
changes (through setMeasurements()), all observers are notified and updated
with the new data.

When to Use the Observer Pattern:


 Event Handling Systems: The observer pattern is ideal when you have an event source
(subject) and multiple event listeners (observers).
Real-Time Systems: Systems where the state of one object frequently changes, and
many other objects need to be updated automatically when that change happens.

 GUI Frameworks: When different GUI components need to respond to user


interactions or other components’ changes without tight coupling.
 Publish/Subscribe Systems: In systems like a news feed, weather data updates, or
stock price updates, where many clients need to be notified when the data changes.
Real-World Use Cases:
 Java Event Handling: The Observer pattern is widely used in Java’s event handling
mechanism, where UI components (like buttons) are observers of user actions
(events).
 Stock Market/Weather Monitoring: Systems that need to notify multiple subscribers
about updates to stock prices, weather conditions, etc.
 News Feeds or Social Media: Systems where users are notified about new posts or
updates from other users.
 Push Notification Systems: Applications that notify users when there is new
information, updates, or alerts.

STATE PATTERN DESIGN


The State Pattern is a behavioral design pattern that allows an object to alter its
behavior when its internal state changes. The object will appear to change its class because
the behavior depends on the state in which it is. The State Pattern is useful when an object's
behavior is dependent on its state, and it must change its behavior when its state changes.
This pattern allows you to create a more modular and flexible system by encapsulating state-
specific behavior in separate classes.

Intent of the State Pattern:


 To allow an object to change its behavior based on its internal state.
 To eliminate conditional statements (if, switch, etc.) that would otherwise be used to
manage state transitions.
 To improve the code maintainability by isolating the state-specific behavior into
separate classes.

Key Components of the State Pattern:


1. Context:
o The Context class represents the object whose behavior changes based on its
state. It maintains a reference to the current state object.
o It provides methods to change the state and delegate state-specific behavior to
the current state.
2. State:
o The State interface defines the behavior that different concrete states must
implement. It typically includes methods that the Context calls to delegate
behavior based on the current state.
3. ConcreteState:
o Each concrete state class implements the State interface and provides its own
specific implementation of the behavior based on the current state.
4. Client:
o The client interacts with the Context and triggers state changes by calling
methods that the Context exposes. The client does not need to be aware of the
internal states of the Context.

How It Works:
1. The Context class holds a reference to the current state.
2. The State interface defines methods for handling different behaviors, which are
implemented by concrete state classes.
3. The Context delegates the request to the current state.
4. The state classes implement the state-specific behavior. The Context can switch
between states by updating its reference to the appropriate state object.

Benefits of the State Pattern:


 Encapsulation: It encapsulates state-specific behavior in separate state classes,
making the code more modular and easier to manage.
 Simplifies conditional logic: It avoids complex conditional logic (like if or switch
statements) that would otherwise handle state transitions.
 Extensibility: Adding new states to the system is easy and does not require modifying
the existing classes.

Drawbacks:
 Increased number of classes: You end up creating many state classes, which may
increase the number of classes in the system.
 Complexity in small systems: For simple systems with minimal state changes, the state
pattern might add unnecessary complexity.

Example of State Pattern in Java:


Let’s implement a simple Gumball Machine example, where the behavior of the machine
depends on whether it is in the NoQuarterState, HasQuarterState, or SoldOutState.

Java Code Example:


// State Interface
interface State {
void insertQuarter();
void ejectQuarter();
void turnCrank();
void dispense();
}

// Concrete State: NoQuarterState


class NoQuarterState implements State {
private GumballMachine gumballMachine;

public NoQuarterState(GumballMachine gumballMachine) {


this.gumballMachine = gumballMachine;
}

@Override
public void insertQuarter() {
System.out.println("You inserted a quarter.");
gumballMachine.setState(gumballMachine.getHasQuarterState());
}

@Override
public void ejectQuarter() {
System.out.println("You haven't inserted a quarter.");
}

@Override
public void turnCrank() {
System.out.println("You turned the crank but there's no quarter.");
}

@Override
public void dispense() {
System.out.println("You need to insert a quarter first.");
}
}

// Concrete State: HasQuarterState


class HasQuarterState implements State {
private GumballMachine gumballMachine;

public HasQuarterState(GumballMachine gumballMachine) {


this.gumballMachine = gumballMachine;
}

@Override
public void insertQuarter() {
System.out.println("You can't insert another quarter.");
}
@Override
public void ejectQuarter() {
System.out.println("Quarter returned.");
gumballMachine.setState(gumballMachine.getNoQuarterState());
}

@Override
public void turnCrank() {
System.out.println("You turned the crank.");
gumballMachine.setState(gumballMachine.getSoldOutState());
}

@Override
public void dispense() {
System.out.println("No gumball dispensed.");
}
}

// Concrete State: SoldOutState


class SoldOutState implements State {
private GumballMachine gumballMachine;

public SoldOutState(GumballMachine gumballMachine) {


this.gumballMachine = gumballMachine;
}

@Override
public void insertQuarter() {
System.out.println("Sorry, the machine is sold out.");
}

@Override
public void ejectQuarter() {
System.out.println("You haven't inserted a quarter.");
}

@Override
public void turnCrank() {
System.out.println("The machine is sold out, no gumball dispensed.");
}

@Override
public void dispense() {
System.out.println("No gumball dispensed.");
}
}

// Context Class: GumballMachine


class GumballMachine {
private State noQuarterState;
private State hasQuarterState;
private State soldOutState;

private State currentState;

public GumballMachine() {
noQuarterState = new NoQuarterState(this);
hasQuarterState = new HasQuarterState(this);
soldOutState = new SoldOutState(this);

currentState = noQuarterState;
}

public void setState(State state) {


this.currentState = state;
}

public State getNoQuarterState() {


return noQuarterState;
public State getHasQuarterState() {
return hasQuarterState;
}

public State getSoldOutState() {


return soldOutState;
}

public void insertQuarter() {


currentState.insertQuarter();
}

public void ejectQuarter() {


currentState.ejectQuarter();
}

public void turnCrank() {


currentState.turnCrank();
currentState.dispense();
}
}

// Client code
public class StatePatternExample {
public static void main(String[] args) {
GumballMachine gumballMachine = new GumballMachine();

gumballMachine.insertQuarter();
gumballMachine.turnCrank();

System.out.println("\n--- Inserting another quarter ---");


gumballMachine.insertQuarter();
gumballMachine.ejectQuarter();
gumballMachine.turnCrank();

System.out.println("\n--- Trying to insert a quarter into a sold-out machine ---");


gumballMachine.setState(gumballMachine.getSoldOutState());
gumballMachine.insertQuarter();
}
}

Explanation of the Code:


1. State Interface:
o The State interface defines the methods that each state (e.g., insertQuarter(),
ejectQuarter(), turnCrank(), and dispense()) will implement.
2. ConcreteState Classes:
NoQuarterState, HasQuarterState, and SoldOutState are concrete
implementations of the State interface. Each of these classes defines how the
o

gumball machine behaves in different states.


o For example, in the NoQuarterState, the machine doesn't allow the user to turn
the crank, while in the HasQuarterState, the machine allows the user to turn
the crank to get a gumball.
3. Context (GumballMachine):
o The GumballMachine class is the context in this pattern. It maintains a
reference to the current state and provides methods like insertQuarter(),
ejectQuarter(), and turnCrank(), which delegate the behavior to the current
state.
4. Client Code:
o The client interacts with the GumballMachine by inserting quarters and turning
the crank. The GumballMachine delegates the behavior to the appropriate state
objects, which alter the machine’s behavior based on the current state.

When to Use the State Pattern:


 When an object’s behavior depends on its state: The object should alter its behavior
depending on its current state, and using conditionals would make the code harder to
maintain.
 To avoid large conditional statements: When you have large switch or if-else
statements managing different states, the State Pattern can help by encapsulating the
state-specific logic in separate classes.
 When the state transitions are complex: If there are many different states with
complex transitions, the State Pattern can help in managing them by delegating state-
specific behavior to individual state classes.

Real-World Use Cases:


 State Machines: For applications with a defined set of states and transitions (e.g.,
workflows, traffic lights, game states).
 GUI Elements: When the behavior of a GUI component (like buttons, text fields)
depends on the state (e.g., enabled/disabled, visible/invisible).
 Vending Machines: Similar to the example above, the state pattern can be used in
vending machines, gumball machines, or ticket machines, where the machine's
behavior changes depending on whether it's waiting for payment, dispensing an item,
etc.

STRATEGY PATTERN DESIGN


The Strategy Pattern is a behavioral design pattern that defines a family of algorithms,
encapsulates each one, and makes them interchangeable. The strategy pattern allows a
client to choose an algorithm from a family of algorithms at runtime without altering the
class that uses it. This pattern is often used when you have multiple ways to perform a task,
and the choice of which one to use depends on some context or condition.

Intent of the Strategy Pattern:


 To define a set of algorithms, encapsulate each one, and make them interchangeable.
 To allow clients to select a specific algorithm or strategy to use at runtime.
 To eliminate conditional statements (like if/switch) for selecting an algorithm and
instead make the selection more flexible and maintainable.

Key Components of the Strategy Pattern:


1. Strategy:
The Strategy interface defines a common interface for all concrete strategies
(algorithms).
o

o It declares a method (e.g., execute()) that each concrete strategy implements.


2. ConcreteStrategy:
o Concrete classes that implement the Strategy interface provide different
implementations of the algorithm.
3. Context:
o The Context class maintains a reference to a Strategy object and delegates the
algorithm to the Strategy. It can change the strategy at runtime.
o The Context class is responsible for calling the Strategy's algorithm, but it
doesn't know the details of how the algorithm works.
How It Works:
1. The Context has a reference to a Strategy object, which implements a specific
algorithm.
2. The Context can switch between different strategies at runtime.
3. The client can choose a strategy and the algorithm is executed without the need for
the client to know the specifics of the algorithm's implementation.

Benefits of the Strategy Pattern:


 Encapsulation: It encapsulates algorithms and makes them easier to manage.
 Flexibility: You can change the algorithm dynamically at runtime.
 Avoids Conditional Logic: By encapsulating algorithms in different strategy classes,
the pattern avoids the use of large switch/if statements that decide which algorithm to
use.
 Extensibility: Adding new strategies (algorithms) is easy without modifying existing
code.

Drawbacks:
 Increased number of classes: The pattern may increase the number of classes,
especially if there are many different algorithms.
 Complexity: In small systems with few algorithms, the strategy pattern may add
unnecessary complexity.

Example of Strategy Pattern in Java:


Let's create an example of a payment system that supports multiple payment strategies (e.g.,
CreditCardPayment, PayPalPayment, BitcoinPayment).

Java Code Example:


// Strategy Interface
interface PaymentStrategy {
void pay(int amount);
}

// Concrete Strategy 1: CreditCardPayment


class CreditCardPayment implements PaymentStrategy {
private String cardNumber;

public CreditCardPayment(String cardNumber) {


this.cardNumber = cardNumber;
}

@Override
public void pay(int amount) {
System.out.println("Paid " + amount + " using Credit Card: " + cardNumber);
}
}

// Concrete Strategy 2: PayPalPayment


class PayPalPayment implements PaymentStrategy {
private String email;

public PayPalPayment(String email) {


this.email = email;
}

@Override
public void pay(int amount) {
System.out.println("Paid " + amount + " using PayPal: " + email);
}
}

// Concrete Strategy 3: BitcoinPayment


class BitcoinPayment implements PaymentStrategy {
private String bitcoinAddress;

public BitcoinPayment(String bitcoinAddress) {


this.bitcoinAddress = bitcoinAddress;
}
@Override
public void pay(int amount) {
System.out.println("Paid " + amount + " using Bitcoin to address: " + bitcoinAddress);
}
}

// Context Class
class PaymentContext {
private PaymentStrategy paymentStrategy;

public PaymentContext(PaymentStrategy paymentStrategy) {


this.paymentStrategy = paymentStrategy;
}

// Set the payment strategy


public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
this.paymentStrategy = paymentStrategy;
}

// Execute payment using the current strategy


public void executePayment(int amount) {
paymentStrategy.pay(amount);
}
}

// Client Code
public class StrategyPatternExample {
public static void main(String[] args) {
// Payment through CreditCard
PaymentContext paymentContext = new PaymentContext(new CreditCardPayment("1234-5678-
9876-5432"));
paymentContext.executePayment(500);

System.out.println("\n--- Switching to PayPal Payment ---");

// Switching to PayPal payment


paymentContext.setPaymentStrategy(new PayPalPayment("[email protected]"));
paymentContext.executePayment(300);

System.out.println("\n--- Switching to Bitcoin Payment ---");

// Switching to Bitcoin payment


paymentContext.setPaymentStrategy(new
BitcoinPayment("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"));
paymentContext.executePayment(200);

Explanation of the Code:


1. Strategy Interface (PaymentStrategy):
o The PaymentStrategy interface defines the pay() method, which is implemented
by each concrete payment strategy class.
2. Concrete Strategy Classes:
o CreditCardPayment, PayPalPayment, and BitcoinPayment are concrete classes
that implement the PaymentStrategy interface. Each class defines its own
version of the pay() method to handle payments using different methods (credit
card, PayPal, Bitcoin).
3. Context Class (PaymentContext):
o The PaymentContext class maintains a reference to a PaymentStrategy object. It
allows setting a specific strategy (setPaymentStrategy()) and delegates the
payment process to the current strategy using the executePayment() method.
4. Client Code:
o In the client code, we create an instance of PaymentContext and set different
payment strategies. We then execute payments using different strategies (credit
card, PayPal, and Bitcoin) based on the current strategy set in the context.

When to Use the Strategy Pattern:


 Multiple ways to perform a task: When you have multiple algorithms or methods to
perform a task and the client needs to choose the best one at runtime.
 Avoiding conditional statements: When you have a large number of if or switch
statements to determine the behavior, the strategy pattern can replace them by
encapsulating each behavior in a separate strategy class.
 Object behavior needs to change dynamically: When you need to dynamically switch
between behaviors based on the context or conditions at runtime.

Real-World Use Cases:


 Payment Systems: As shown in the example above, payment systems that support
multiple payment methods (credit card, PayPal, Bitcoin, etc.).
 Sorting Algorithms: In a sorting application, different sorting strategies (e.g., bubble
sort, quick sort, merge sort) can be implemented using the strategy pattern, where the
user can choose which sorting algorithm to apply.
 Compression Algorithms: For applications that support different file compression
formats (ZIP, GZIP, TAR), the strategy pattern allows switching between compression
algorithms at runtime.
 Navigation Systems: In a navigation system, different routing strategies (shortest
path, fastest route, scenic route) can be used depending on user preferences.

You might also like