1. Singleton Pattern
Usage: For classes that should have only one instance during the application lifecycle, like database connection pools, logging, or caching.
Example:
DatabaseConnectionManager: Ensures only one instance of the database connection manager exists across the entire application to handle connections efficiently.
public class DatabaseConnectionManager {
private static DatabaseConnectionManager instance;
private DatabaseConnectionManager() {
// Initialize database connection
}
public static synchronized DatabaseConnectionManager getInstance() {
if (instance == null) {
instance = new DatabaseConnectionManager();
}
return instance;
}
// Other database related methods
}
2.Factory Pattern
Usage: To create objects without exposing the instantiation logic to the client and refer to the newly created object through a common interface.
Example:
PaymentFactory: For handling multiple payment gateways such as PayPal, Credit Cards, or UPI, you could use a factory to return the appropriate payment processor.
public class PaymentFactory { public static PaymentProcessor getPaymentProcessor(String type) { switch (type) { case "CreditCard": return new CreditCardProcessor(); case "UPI": return new UpiProcessor(); case "PayPal": return new PayPalProcessor(); default: throw new IllegalArgumentException("Unknown payment type"); } } }
3. Observer Pattern
Usage: To notify various services of changes in a particular entity's state, without the services tightly coupling to that entity.
Example:
Booking Notifications: Notify users via SMS, email, and push notifications when they successfully book a ticket.
public interface Observer { void update(String message); } public class EmailNotifier implements Observer { @Override public void update(String message) { System.out.println("Email sent with message: " + message); } } public class SmsNotifier implements Observer { @Override public void update(String message) { System.out.println("SMS sent with message: " + message); } } public class Booking { private List<Observer> observers = new ArrayList<>(); public void addObserver(Observer observer) { observers.add(observer); } public void removeObserver(Observer observer) { observers.remove(observer); } public void notifyObservers(String message) { for (Observer observer : observers) { observer.update(message); } } public void completeBooking() { // Booking logic here notifyObservers("Booking completed successfully!"); } }
4. Strategy Pattern
Usage: For defining a family of algorithms, encapsulating each one, and making them interchangeable.
Example:
Discount Strategy: Different pricing strategies for weekdays, weekends, and holidays.
public interface PricingStrategy { double calculatePrice(double basePrice); } public class WeekendPricingStrategy implements PricingStrategy { @Override public double calculatePrice(double basePrice) { return basePrice * 1.2; // 20% higher on weekends } } public class WeekdayPricingStrategy implements PricingStrategy { @Override public double calculatePrice(double basePrice) { return basePrice; // Regular pricing on weekdays } } public class Booking { private PricingStrategy pricingStrategy; public Booking(PricingStrategy pricingStrategy) { this.pricingStrategy = pricingStrategy; } public double calculateFinalPrice(double basePrice) { return pricingStrategy.calculatePrice(basePrice); } }
5. Command Pattern
Usage: To encapsulate requests as objects, thereby allowing for the parameterization of clients with different requests, queuing of requests, and logging the requests.
Example:
Booking Commands: Handle booking-related commands like creating a booking, canceling, and modifying a booking.
public interface Command { void execute(); } public class CreateBookingCommand implements Command { private BookingService bookingService; public CreateBookingCommand(BookingService bookingService) { this.bookingService = bookingService; } @Override public void execute() { bookingService.createBooking(); } } public class CancelBookingCommand implements Command { private BookingService bookingService; public CancelBookingCommand(BookingService bookingService) { this.bookingService = bookingService; } @Override public void execute() { bookingService.cancelBooking(); } } public class BookingService { public void createBooking() { // Logic for creating booking } public void cancelBooking() { // Logic for canceling booking } }
6. Decorator Pattern
Usage: To add behavior to an object dynamically, without affecting the behavior of other objects from the same class.
Example:
Add-ons to a Ticket Booking: Add features like food combos or seat upgrades as add-ons to a base ticket booking.
public interface Ticket { double getPrice(); String getDescription(); } public class BasicTicket implements Ticket { @Override public double getPrice() { return 200.00; } @Override public String getDescription() { return "Basic Ticket"; } } public class PopcornDecorator extends TicketDecorator { public PopcornDecorator(Ticket ticket) { super(ticket); } @Override public double getPrice() { return super.getPrice() + 50; // Add cost of popcorn } @Override public String getDescription() { return super.getDescription() + ", with Popcorn"; } }