What are Design Patterns in Java?
In Java, a design pattern is a standard, reusable solution to a recurring software design problem.
- They are not code, but templates or best practices that you can adapt to your project.
- Design patterns help make code more flexible, reusable, and easier to maintain.
- They solve problems related to object creation, object composition, and communication between objects.
Why Use Them?
- Avoid reinventing the wheel
- Improve code readability
- Provide a shared vocabulary among developers
- Make systems more robust and adaptable to change
- Nickname for the four authors of the landmark 1994 book:
“Design Patterns: Elements of Reusable Object-Oriented Software” - Erich Gamma
- Richard Helm
- Ralph Johnson
- John Vlissides
- This book introduced the term “design patterns” to the programming world in a formal way and documented 23 classic patterns.
Why GoF is Important in Java
- Java, being an object-oriented language, naturally benefits from the GoF patterns.
- Most Java frameworks (Spring, Hibernate, etc.) use multiple GoF patterns internally.
- The GoF book became the foundation for modern software design principles.
Categories of Design Patterns
Category | Purpose |
Creational | Handle object creation in a way that hides complexity and makes the system independent of object creation details. |
Structural | Focus on class and object composition — building larger structures from smaller parts. |
Behavioral | Focus on communication between objects and responsibility distribution. |
Creational (5)
1. Singleton – Ensures only one instance exists and provides global access.
2. Factory Method – Creates objects without specifying the exact class.
3. Abstract Factory – Creates families of related objects without specifying classes.
4. Builder – Constructs complex objects step-by-step.
5. Prototype – Creates new objects by cloning an existing object.
Structural (7)
6. Adapter – Allows incompatible interfaces to work together.
7. Bridge – Separates abstraction from implementation.
8. Composite – Treats individual objects and object groups uniformly.
9. Decorator – Adds new behavior to objects dynamically.
10. Facade – Provides a simplified interface to a complex system.
11. Flyweight – Minimizes memory by sharing common object state.
12. Proxy – Provides a placeholder or surrogate for another object.
Behavioral (11)
13. Chain of Responsibility – Passes request along a chain until handled.
14. Command – Encapsulates a request as an object.
15. Interpreter – Defines grammar and interprets sentences.
16. Iterator – Sequentially accesses elements without exposing structure.
17. Mediator – Centralizes complex communications between objects.
18. Memento – Captures and restores an object’s state.
19. Observer – Notifies multiple objects when a state changes.
20. State – Alters behavior when internal state changes.
21. Strategy – Encapsulates interchangeable algorithms.
22. Template Method – Defines algorithm skeleton, deferring steps to subclasses.
23. Visitor – Separates algorithms from object structures.
b) Non-GoF Patterns
Enterprise / Java EE Patterns
24. DAO – Separates persistence logic from business logic.
25. DTO – Transfers data between layers without exposing domain models.
26. Service Locator – Central registry for service lookups.
27. Business Delegate – Simplifies client interaction with business services.
28. Repository – Manages collection-like access to domain objects.
29. Factory Bean – Spring-specific configurable object creator.
30. Dependency Injection – Injects object dependencies at runtime.
31. Lazy Initialization – Defers object creation until needed.
32. Front Controller – Central request handler in web apps.
33. Intercepting Filter – Pre/post-processing of web requests.
34. Composite Entity – Represents a graph of objects in EJB.
35. Value Object – Immutable object for holding data.
36. Session Facade – Simplifies EJB interactions.
Concurrency Patterns
37. Producer–Consumer – Separates producers of data from consumers.
38. Thread Pool – Manages a pool of reusable threads.
39. Read–Write Lock – Allows concurrent reads but exclusive writes.
40. Future – Represents a result of an async computation.
41. Scheduler – Manages timed or recurring tasks.
42. Reactor – Handles service requests concurrently using event demultiplexing.
43. Guarded Suspension – Waits for a condition before execution.
44. Balking – Avoids executing an action if the system is in the wrong state.
Architectural Patterns
45. MVC – Separates application into Model, View, Controller.
46. MVVM – Separates Model, View, ViewModel.
47. MVP – Separates Model, View, Presenter.
48. Layered Architecture – Organizes system into layers.
49. Hexagonal Architecture – Decouples core logic from external systems.
50. Microkernel Architecture – Minimal core with plug-in modules.
51. Microservices – Distributed services with independent deployment.
52. Event-Driven – Uses events to trigger actions asynchronously.
53. CQRS – Separates command and query responsibilities.
54. Pipe & Filter – Chains processing steps via data streams.
Integration Patterns
55. API Gateway – Single entry point for APIs.
56. Circuit Breaker – Prevents cascading failures in distributed systems.
57. Saga – Manages distributed transactions in microservices.
58. Publish–Subscribe – One-to-many asynchronous messaging.
59. Message Broker – Mediates message passing between systems.
60. Service Registry & Discovery – Keeps track of service locations.
61. Strangler Fig – Incremental migration from legacy to new system.
Creational GoF patterns (5)
1. Singleton Pattern
Intent:
Ensure a class has only one instance and provide a global access point to it.
Example Problem:
A logging service, configuration manager, or database connection pool should have only one instance to avoid inconsistent states.
Java Example:
public class Logger {
private static Logger instance;
private Logger() {} // private constructor
public static Logger getInstance() {
if (instance == null) {
instance = new Logger();
}
return instance;
}
public void log(String message) {
System.out.println("[LOG] " + message);
}
}
class Main {
public static void main(String[] args) {
Logger logger = Logger.getInstance();
logger.log("Application started");
}
}
Notes:
- Can be lazy-loaded (as above) or eager-loaded.
- In multithreaded environments, use synchronized or Double-Checked Locking.
Real-Time Use Cases:
- Spring Beans (default scope is singleton)
- Logging frameworks (Log4j, SLF4J)
- Database connection pools (HikariDataSource)
- Caching services (Ehcache, RedisTemplate)
2. Factory Method Pattern
Intent:
Define an interface for creating an object, but let subclasses decide which class to instantiate.
Example Problem:
A payment gateway should support different payment types (Credit Card, UPI, PayPal) without the calling code knowing the exact class.
Java Example:
interface Payment {
void pay(double amount);
}
class UpiPayment implements Payment {
public void pay(double amount) { System.out.println("Paid via UPI: " + amount); }
}
abstract class PaymentFactory {
abstract Payment createPayment();
}
class UpiPaymentFactory extends PaymentFactory {
public Payment createPayment() { return new UpiPayment(); }
}
class Main {
public static void main(String[] args) {
PaymentFactory factory = new UpiPaymentFactory();
Payment payment = factory.createPayment();
payment.pay(500);
}
}
Notes:
- Promotes loose coupling and easy extension.
- Only knows the interface, not the implementation.
Real-Time Use Cases:
- Calendar.getInstance() in Java.
- Spring’s BeanFactory for bean creation.
- Payment processing systems supporting multiple providers.
3. Abstract Factory Pattern
Intent:
Provide an interface to create families of related objects without specifying concrete classes.
Example Problem:
A UI framework should generate Windows or Mac components depending on the OS.
Java Example:
interface Button { void paint(); }
interface Checkbox { void render(); }
class WinButton implements Button {
public void paint() { System.out.println("Windows Button"); }
}
class WinCheckbox implements Checkbox {
public void render() { System.out.println("Windows Checkbox"); }
}
interface GUIFactory {
Button createButton();
Checkbox createCheckbox();
}
class WinFactory implements GUIFactory {
public Button createButton() { return new WinButton(); }
public Checkbox createCheckbox() { return new WinCheckbox(); }
}
class Main {
public static void main(String[] args) {
GUIFactory factory = new WinFactory();
factory.createButton().paint();
factory.createCheckbox().render();
}
}
Notes:
- Ensures that related components are compatible.
- Good for product families with variations.
Real-Time Use Cases:
- JDBC drivers (DriverManager.getConnection() returns DB-specific objects).
- Spring @Profile to load environment-specific beans.
- Cross-platform UI toolkits.
4. Builder Pattern
Intent:
Separate the construction of a complex object from its representation so the same process can create different configurations.
Example Problem:
Building a RestTemplate with optional configurations like timeout and interceptors.
Java Example:
class RestClient {
String baseUrl;
int timeout;
static class Builder {
String baseUrl;
int timeout;
Builder baseUrl(String url) { this.baseUrl = url; return this; }
Builder timeout(int t) { this.timeout = t; return this; }
RestClient build() { return new RestClient(this); }
}
private RestClient(Builder b) {
this.baseUrl = b.baseUrl;
this.timeout = b.timeout;
}
}
class Main {
public static void main(String[] args) {
RestClient client = new RestClient.Builder()
.baseUrl("https://api.example.com")
.timeout(5000)
.build();
}
}
Notes:
- Avoids telescoping constructor problem.
- Great for immutable objects.
Real-Time Use Cases:
- StringBuilder in Java.
- Lombok’s @Builder for DTOs.
- Spring’s UriComponentsBuilder.
5. Prototype Pattern
Intent:
Create new objects by copying an existing object instead of creating from scratch.
Example Problem:
Cloning a configuration object for testing with small modifications.
Java Example:
class Report implements Cloneable {
String title;
Report(String title) { this.title = title; }
public Report clone() {
try { return (Report) super.clone(); }
catch (CloneNotSupportedException e) { return null; }
}
}
class Main {
public static void main(String[] args) {
Report r1 = new Report("Sales Q1");
Report r2 = r1.clone();
System.out.println(r2.title);
}
}
Notes:
- Useful when object creation is expensive.
- Watch out for deep vs shallow copy.
Real-Time Use Cases:
- Spring @Scope("prototype") beans.
- Document/template duplication.
- Game object cloning.
Structural GoF patterns (7)
1. Adapter Pattern
Intent:
Convert the interface of a class into another interface clients expect.
Example Problem:
You have a legacy payment service that processes in rupees, but your new system expects amounts in dollars.
Java Example:
interface PaymentGateway {
void payUSD(double amount);
}
class RupeePayment {
void payINR(double amount) {
System.out.println("Paid ₹" + amount);
}
}
class PaymentAdapter implements PaymentGateway {
private RupeePayment rupeePayment = new RupeePayment();
public void payUSD(double amount) {
rupeePayment.payINR(amount * 83); // Convert USD → INR
}
}
class Main {
public static void main(String[] args) {
PaymentGateway gateway = new PaymentAdapter();
gateway.payUSD(10);
}
}
Notes:
- Acts as a bridge between incompatible interfaces.
- Doesn’t change existing classes.
Real-Time Use Cases:
- InputStreamReader / OutputStreamWriter in Java I/O.
- Spring’s AdapterPattern in HandlerAdapter.
- Integrating old APIs into new systems.
2. Bridge Pattern
Intent:
Decouple an abstraction from its implementation so both can vary independently.
Example Problem:
A remote control should work for multiple TV brands without hardcoding brand-specific logic in the remote.
Java Example:
interface Device { void turnOn(); }
class SonyTV implements Device { public void turnOn() { System.out.println("Sony TV ON"); } }
class LGTV implements Device { public void turnOn() { System.out.println("LG TV ON"); } }
abstract class Remote {
protected Device device;
Remote(Device device) { this.device = device; }
abstract void pressOn();
}
class BasicRemote extends Remote {
BasicRemote(Device device) { super(device); }
void pressOn() { device.turnOn(); }
}
class Main {
public static void main(String[] args) {
Remote remote = new BasicRemote(new SonyTV());
remote.pressOn();
}
}
Notes:
- Splits abstraction (Remote) from implementation (Device).
- Easier to extend both sides.
Real-Time Use Cases:
- JDBC API (abstraction) with multiple drivers (implementations).
- Spring JdbcTemplate with different databases.
- Graphics libraries supporting multiple rendering engines.
3. Composite Pattern
Intent:
Compose objects into tree structures to represent part-whole hierarchies.
Example Problem:
Represent a folder structure where folders can contain files and subfolders.
Java Example:
import java.util.*;
interface FileComponent {
void showDetails();
}
class FileLeaf implements FileComponent {
String name;
FileLeaf(String name) { this.name = name; }
public void showDetails() { System.out.println("File: " + name); }
}
class Folder implements FileComponent {
String name;
List<FileComponent> children = new ArrayList<>();
Folder(String name) { this.name = name; }
public void add(FileComponent c) { children.add(c); }
public void showDetails() {
System.out.println("Folder: " + name);
for (FileComponent c : children) c.showDetails();
}
}
class Main {
public static void main(String[] args) {
Folder root = new Folder("Root");
root.add(new FileLeaf("file1.txt"));
Folder sub = new Folder("Sub");
sub.add(new FileLeaf("file2.txt"));
root.add(sub);
root.showDetails();
}
}
Notes:
- Treats individual and composite objects uniformly.
- Ideal for hierarchical data.
Real-Time Use Cases:
- DOM tree in HTML/XML parsing.
- Spring Security role hierarchy.
- File explorers.
Intent:
Attach additional responsibilities to an object dynamically.
Example Problem:
Add compression and encryption features to a file output stream without modifying the original class.
Java Example:
interface DataSource { void write(String data); }
class FileDataSource implements DataSource {
public void write(String data) { System.out.println("Writing: " + data); }
}
class DataSourceDecorator implements DataSource {
protected DataSource wrappee;
DataSourceDecorator(DataSource source) { this.wrappee = source; }
public void write(String data) { wrappee.write(data); }
}
class EncryptionDecorator extends DataSourceDecorator {
EncryptionDecorator(DataSource source) { super(source); }
public void write(String data) { super.write("[Encrypted]" + data); }
}
class Main {
public static void main(String[] args) {
DataSource source = new EncryptionDecorator(new FileDataSource());
source.write("Hello");
}
}
Notes:
- Enhances objects at runtime without altering base code.
- More flexible than subclassing.
Real-Time Use Cases:
- Java I/O Streams (BufferedInputStream, DataOutputStream).
- Spring’s BeanPostProcessor.
- Servlet filters.
5. Facade Pattern
Intent:
Provide a unified interface to a set of interfaces in a subsystem.
Example Problem:
Simplify interaction with multiple complex services like payment, notification, and shipping.
Java Example:
class PaymentService { void process() { System.out.println("Payment processed"); } }
class NotificationService { void send() { System.out.println("Notification sent"); } }
class ShippingService { void ship() { System.out.println("Item shipped"); } }
class OrderFacade {
private PaymentService payment = new PaymentService();
private NotificationService notification = new NotificationService();
private ShippingService shipping = new ShippingService();
void placeOrder() {
payment.process();
notification.send();
shipping.ship();
}
}
class Main {
public static void main(String[] args) {
new OrderFacade().placeOrder();
}
}
Notes:
- Hides subsystem complexity.
- Reduces dependency on internal details.
Real-Time Use Cases:
- java.util.logging.Logger methods.
- Spring’s JdbcTemplate hiding JDBC boilerplate.
- REST client wrappers.
6. Flyweight Pattern
Intent:
Minimize memory by sharing as much data as possible between similar objects.
Example Problem:
Rendering a large forest where each tree shares texture and color data.
Java Example:
class TreeType {
String name, color;
TreeType(String name, String color) { this.name = name; this.color = color; }
}
class Tree {
int x, y;
TreeType type;
Tree(int x, int y, TreeType type) { this.x = x; this.y = y; this.type = type; }
}
class TreeFactory {
static TreeType oakType = new TreeType("Oak", "Green");
static Tree getOak(int x, int y) { return new Tree(x, y, oakType); }
}
class Main {
public static void main(String[] args) {
Tree t1 = TreeFactory.getOak(1, 1);
Tree t2 = TreeFactory.getOak(2, 2);
System.out.println(t1.type == t2.type); // true
}
}
Notes:
- Great for objects with shared state.
- Reduces RAM usage.
Real-Time Use Cases:
- String pool in Java.
- Character objects in text editors.
- Game asset sharing.
7. Proxy Pattern
Intent:
Provide a placeholder or surrogate to control access to another object.
Example Problem:
Control access to an expensive object (like a video loader) until it’s actually needed.
Java Example:
interface Video { void play(); }
class RealVideo implements Video {
String filename;
RealVideo(String filename) { this.filename = filename; load(); }
void load() { System.out.println("Loading " + filename); }
public void play() { System.out.println("Playing " + filename); }
}
class ProxyVideo implements Video {
RealVideo realVideo;
String filename;
ProxyVideo(String filename) { this.filename = filename; }
public void play() {
if (realVideo == null) realVideo = new RealVideo(filename);
realVideo.play();
}
}
class Main {
public static void main(String[] args) {
Video video = new ProxyVideo("movie.mp4");
video.play(); // Loads only when played
}
}
Notes:
- Controls creation or access.
- Can implement lazy loading, access control, logging.
Real-Time Use Cases:
- Hibernate lazy loading.
- Spring AOP proxies.
- Security proxies for authentication.
Behavioral GoF patterns (11)
1. Chain of Responsibility Pattern
Intent:
Pass a request along a chain of handlers until one of them processes it.
Example Problem:
You want different levels of support staff (Level 1, Level 2, Manager) to handle customer complaints.
Java Example:
abstract class SupportHandler {
protected SupportHandler next;
public void setNext(SupportHandler next) { this.next = next; }
public abstract void handleRequest(String issue);
}
class Level1Support extends SupportHandler {
public void handleRequest(String issue) {
if (issue.equals("basic")) System.out.println("Handled by Level 1");
else if (next != null) next.handleRequest(issue);
}
}
class Level2Support extends SupportHandler {
public void handleRequest(String issue) {
if (issue.equals("advanced")) System.out.println("Handled by Level 2");
else if (next != null) next.handleRequest(issue);
}
}
class ManagerSupport extends SupportHandler {
public void handleRequest(String issue) {
System.out.println("Handled by Manager");
}
}
class Main {
public static void main(String[] args) {
SupportHandler l1 = new Level1Support();
SupportHandler l2 = new Level2Support();
SupportHandler mgr = new ManagerSupport();
l1.setNext(l2);
l2.setNext(mgr);
l1.handleRequest("advanced");
}
}
Notes:
- Promotes loose coupling between sender and receivers.
- Order of handlers matters.
Real-Time Use Cases:
- Servlet filter chains.
- Exception handling chains.
- Spring Security filter chain.
2. Command Pattern
Intent:
Encapsulate a request as an object, allowing parameterization and queuing of requests.
Example Problem:
Implement an undo/redo system for a text editor.
Java Example:
interface Command { void execute(); }
class TextEditor {
void write(String text) { System.out.println("Writing: " + text); }
}
class WriteCommand implements Command {
private TextEditor editor; private String text;
WriteCommand(TextEditor editor, String text) { this.editor = editor; this.text = text; }
public void execute() { editor.write(text); }
}
class Main {
public static void main(String[] args) {
TextEditor editor = new TextEditor();
Command cmd = new WriteCommand(editor, "Hello");
cmd.execute();
}
}
Notes:
- Decouples sender and receiver.
- Supports undo/redo, logging, queuing.
Real-Time Use Cases:
- Runnable in Java threading.
- GUI button actions.
- Job scheduling.
3. Interpreter Pattern
Intent:
Define a grammar and interpret sentences in that grammar.
Example Problem:
Build a simple language to check if a word contains a specific letter.
Java Example:
interface Expression { boolean interpret(String context); }
class ContainsExpression implements Expression {
private String data;
ContainsExpression(String data) { this.data = data; }
public boolean interpret(String context) { return context.contains(data); }
}
class Main {
public static void main(String[] args) {
Expression isJava = new ContainsExpression("Java");
System.out.println(isJava.interpret("I love Java")); // true
}
}
Notes:
- Works best for small grammars.
- Can be slow for complex parsing.
Real-Time Use Cases:
- SQL query parsing.
- Regular expression engines.
- Rule engines.
4. Iterator Pattern
Intent:
Provide a way to access elements of a collection without exposing its representation.
Example Problem:
Create a custom iterator for a collection of names.
Java Example:
interface Iterator<T> { boolean hasNext(); T next(); }
interface Container<T> { Iterator<T> getIterator(); }
class NameRepository implements Container<String> {
private String[] names = {"A", "B", "C"};
public Iterator<String> getIterator() {
return new Iterator<String>() {
int index = 0;
public boolean hasNext() { return index < names.length; }
public String next() { return names[index++]; }
};
}
}
class Main {
public static void main(String[] args) {
NameRepository repo = new NameRepository();
Iterator<String> it = repo.getIterator();
while (it.hasNext()) System.out.println(it.next());
}
}
Notes:
- Keeps collection encapsulated.
- Iteration logic separated.
Real-Time Use Cases:
- Java Iterator interface.
- ResultSet iteration in JDBC.
- For-each loops.
5. Mediator Pattern
Intent:
Define an object that encapsulates how a set of objects interact.
Example Problem:
Implement a chat room where users talk to each other via a mediator.
Java Example:
import java.util.*;
class ChatMediator {
private List<User> users = new ArrayList<>();
public void addUser(User user) { users.add(user); }
public void sendMessage(String msg, User sender) {
for (User u : users) if (u != sender) u.receive(msg);
}
}
class User {
private String name;
private ChatMediator mediator;
User(String name, ChatMediator mediator) { this.name = name; this.mediator = mediator; }
public void send(String msg) { mediator.sendMessage(msg, this); }
public void receive(String msg) { System.out.println(name + " received: " + msg); }
}
class Main {
public static void main(String[] args) {
ChatMediator mediator = new ChatMediator();
User u1 = new User("Alice", mediator);
User u2 = new User("Bob", mediator);
mediator.addUser(u1); mediator.addUser(u2);
u1.send("Hello!");
}
}
Notes:
- Reduces coupling between components.
- Centralizes control.
Real-Time Use Cases:
- Chat room applications.
- Air traffic control systems.
- GUI frameworks event handling.
6. Memento Pattern
Intent:
Capture and restore an object’s state without exposing its internals.
Example Problem:
Implement undo functionality for a text editor.
Java Example:
class Editor {
private String content;
public void setContent(String content) { this.content = content; }
public String getContent() { return content; }
public Memento save() { return new Memento(content); }
public void restore(Memento m) { content = m.getState(); }
}
class Memento {
private final String state;
Memento(String state) { this.state = state; }
public String getState() { return state; }
}
class Main {
public static void main(String[] args) {
Editor editor = new Editor();
editor.setContent("A");
Memento saved = editor.save();
editor.setContent("B");
editor.restore(saved);
System.out.println(editor.getContent()); // A
}
}
Notes:
- Keeps encapsulation intact.
- Caretaker manages mementos.
Real-Time Use Cases:
- Undo in editors.
- Game save states.
- Transaction rollback.
7. Observer Pattern
Intent:
Define a dependency so that when one object changes state, all dependents are notified.
Example Problem:
A weather station updates multiple displays when temperature changes.
Java Example:
import java.util.*;
interface Observer { void update(int temp); }
class WeatherStation {
private List<Observer> observers = new ArrayList<>();
private int temperature;
public void addObserver(Observer o) { observers.add(o); }
public void setTemperature(int temp) {
this.temperature = temp;
for (Observer o : observers) o.update(temp);
}
}
class Display implements Observer {
public void update(int temp) { System.out.println("Temp: " + temp); }
}
class Main {
public static void main(String[] args) {
WeatherStation ws = new WeatherStation();
ws.addObserver(new Display());
ws.setTemperature(30);
}
}
Notes:
- Loosely couples subject and observers.
- Pull or push model possible.
Real-Time Use Cases:
- Java Observable.
- Event listeners in Swing.
- Stock market feeds.
8. State Pattern
Intent:
Allow an object to change its behavior when its internal state changes.
Example Problem:
A traffic light changes behavior based on current color.
Java Example:
interface State { void handle(); }
class RedState implements State { public void handle() { System.out.println("Stop!"); } }
class GreenState implements State { public void handle() { System.out.println("Go!"); } }
class TrafficLight {
private State state;
public void setState(State state) { this.state = state; }
public void request() { state.handle(); }
}
class Main {
public static void main(String[] args) {
TrafficLight light = new TrafficLight();
light.setState(new RedState()); light.request();
light.setState(new GreenState()); light.request();
}
}
Notes:
- Replaces big if-else with polymorphism.
- Easier state transitions.
Real-Time Use Cases:
- Vending machines.
- Game AI.
- Workflow engines.
9. Strategy Pattern
Intent:
Define a family of algorithms, encapsulate each one, and make them interchangeable.
Example Problem:
Implement multiple payment methods in an e-commerce app.
Java Example:
interface PaymentStrategy { void pay(int amount); }
class CreditCardPayment implements PaymentStrategy {
public void pay(int amount) { System.out.println("Paid $" + amount + " via Credit Card"); }
}
class PayPalPayment implements PaymentStrategy {
public void pay(int amount) { System.out.println("Paid $" + amount + " via PayPal"); }
}
class PaymentContext {
private PaymentStrategy strategy;
public PaymentContext(PaymentStrategy strategy) { this.strategy = strategy; }
public void pay(int amount) { strategy.pay(amount); }
}
class Main {
public static void main(String[] args) {
PaymentContext ctx = new PaymentContext(new CreditCardPayment());
ctx.pay(100);
ctx = new PaymentContext(new PayPalPayment());
ctx.pay(200);
}
}
Notes:
- Avoids hardcoding algorithm logic.
- Promotes Open/Closed Principle.
Real-Time Use Cases:
- Sorting with different comparators.
- Payment gateways.
- Compression algorithms.
10. Template Method Pattern
Intent:
Define the skeleton of an algorithm, deferring some steps to subclasses.
Example Problem:
Build a generic data processing workflow where specific steps vary.
Java Example:
abstract class DataProcessor {
public final void process() {
readData();
processData();
saveData();
}
abstract void readData();
abstract void processData();
void saveData() { System.out.println("Saving processed data"); }
}
class CSVProcessor extends DataProcessor {
void readData() { System.out.println("Reading CSV"); }
void processData() { System.out.println("Processing CSV"); }
}
class Main {
public static void main(String[] args) {
DataProcessor p = new CSVProcessor();
p.process();
}
}
Notes:
- Enforces overall structure.
- Some steps fixed, some customizable.
Real-Time Use Cases:
- HttpServlet doGet/doPost in Java.
- Build systems.
- Game AI routines.
11. Visitor Pattern
Intent:
Separate an algorithm from the object structure it operates on.
Example Problem:
Perform operations (like tax calculation, price display) on different product types without modifying them.
Java Example:
interface Visitor { void visit(Book book); void visit(Fruit fruit); }
interface Item { void accept(Visitor visitor); }
class Book implements Item {
public void accept(Visitor visitor) { visitor.visit(this); }
}
class Fruit implements Item {
public void accept(Visitor visitor) { visitor.visit(this); }
}
class PriceVisitor implements Visitor {
public void visit(Book book) { System.out.println("Book price calculated"); }
public void visit(Fruit fruit) { System.out.println("Fruit price calculated"); }
}
class Main {
public static void main(String[] args) {
Item[] items = {new Book(), new Fruit()};
Visitor visitor = new PriceVisitor();
for (Item i : items) i.accept(visitor);
}
}
Notes:
- Good when operations change often but object structure remains stable.
- Can be verbose.
Real-Time Use Cases:
- AST processing in compilers.
- Document processing.
- UI component rendering.