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());
}
}