Discuss common design patterns and their implementation in Java.

Introduction
- Overview of design patterns in software development.
- Why design patterns are crucial for writing scalable, maintainable, and reusable code.
- Categories of design patterns:
- Creational — Object creation mechanisms.
- Structural — Class and object composition.
- Behavioral — Communication between objects.
1. Creational Design Patterns
1.1 Singleton Pattern
- Ensures that only one instance of a class is created.
- Used for logging, database connections, and configuration management.
Implementation:
javapublic class Singleton {
private static Singleton instance;
private Singleton() {} // Private constructor public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}✅ Use Case: Database connection manager.
1.2 Factory Pattern
- Provides an interface for creating objects without specifying their exact class.
Implementation:
javainterface Shape {
void draw();
}class Circle implements Shape {
public void draw() { System.out.println("Drawing Circle"); }
}class Square implements Shape {
public void draw() { System.out.println("Drawing Square"); }
}class ShapeFactory {
public static Shape getShape(String type) {
if (type.equalsIgnoreCase("CIRCLE")) return new Circle();
if (type.equalsIgnoreCase("SQUARE")) return new Square();
return null;
}
}// Usage
Shape shape = ShapeFactory.getShape("CIRCLE");
shape.draw();
✅ Use Case: UI component creation (e.g., buttons, text fields).
1.3 Builder Pattern
- Used for constructing complex objects step by step.
Implementation:
javaclass Car {
private String engine;
private int wheels; public static class Builder {
private String engine;
private int wheels; public Builder setEngine(String engine) {
this.engine = engine;
return this;
} public Builder setWheels(int wheels) {
this.wheels = wheels;
return this;
} public Car build() {
return new Car(this);
}
} private Car(Builder builder) {
this.engine = builder.engine;
this.wheels = builder.wheels;
}
}// Usage
Car car = new Car.Builder().setEngine("V8").setWheels(4).build();
✅ Use Case: Configuring objects with multiple optional parameters (e.g., HTTP requests).
2. Structural Design Patterns
2.1 Adapter Pattern
- Allows incompatible interfaces to work together.
Implementation:
javainterface MediaPlayer {
void play(String audioType);
}class MP3Player implements MediaPlayer {
public void play(String audioType) {
System.out.println("Playing MP3 file");
}
}class MP4Player {
void playMP4() {
System.out.println("Playing MP4 file");
}
}class MediaAdapter implements MediaPlayer {
private MP4Player mp4Player; public MediaAdapter() {
mp4Player = new MP4Player();
} public void play(String audioType) {
if (audioType.equalsIgnoreCase("MP4")) {
mp4Player.playMP4();
}
}
}// Usage
MediaPlayer player = new MediaAdapter();
player.play("MP4");
✅ Use Case: Integrating third-party libraries.
2.2 Decorator Pattern
- Dynamically adds behavior to objects.
Implementation:
javainterface Coffee {
String getDescription();
double cost();
}class SimpleCoffee implements Coffee {
public String getDescription() { return "Simple Coffee"; }
public double cost() { return 5.0; }
}class MilkDecorator implements Coffee {
private Coffee coffee; public MilkDecorator(Coffee coffee) {
this.coffee = coffee;
} public String getDescription() { return coffee.getDescription() + ", Milk"; }
public double cost() { return coffee.cost() + 1.5; }
}// Usage
Coffee coffee = new MilkDecorator(new SimpleCoffee());
System.out.println(coffee.getDescription() + " - $" + coffee.cost());
✅ Use Case: Adding toppings to a coffee order in an online coffee shop.
3. Behavioral Design Patterns
3.1 Observer Pattern
- Defines a one-to-many dependency between objects.
Implementation:
javaimport java.util.ArrayList;
import java.util.List;interface Observer {
void update(String message);
}class Subscriber implements Observer {
private String name; public Subscriber(String name) {
this.name = name;
} public void update(String message) {
System.out.println(name + " received: " + message);
}
}class Publisher {
private List<Observer> observers = new ArrayList<>(); public void addObserver(Observer observer) {
observers.add(observer);
} public void notifyObservers(String message) {
for (Observer observer : observers) {
observer.update(message);
}
}
}// Usage
Publisher newsPublisher = new Publisher();
Observer user1 = new Subscriber("Alice");
Observer user2 = new Subscriber("Bob");
newsPublisher.addObserver(user1);
newsPublisher.addObserver(user2);
newsPublisher.notifyObservers("New article published!");✅ Use Case: Event-driven notifications (e.g., stock market updates, messaging apps).
3.2 Strategy Pattern
- Defines a family of algorithms, encapsulates them, and makes them interchangeable.
Implementation:
javainterface PaymentStrategy {
void pay(int amount);
}class CreditCardPayment implements PaymentStrategy {
public void pay(int amount) {
System.out.println("Paid $" + amount + " using Credit Card.");
}
}class PayPalPayment implements PaymentStrategy {
public void pay(int amount) {
System.out.println("Paid $" + amount + " using PayPal.");
}
}class PaymentContext {
private PaymentStrategy strategy; public void setPaymentStrategy(PaymentStrategy strategy) {
this.strategy = strategy;
} public void pay(int amount) {
strategy.pay(amount);
}
}// Usage
PaymentContext context = new PaymentContext();
context.setPaymentStrategy(new PayPalPayment());
context.pay(100);
✅ Use Case: Payment gateway selection (Credit Card, PayPal, etc.).
Conclusion
- Design patterns enhance code reusability, scalability, and maintainability.
- Each pattern solves a specific design problem efficiently.
- Choosing the right pattern is key to writing better Java applications.
- Explore Java Design Pattern Libraries like Spring Framework.
- Implement patterns in real-world projects.
- Experiment with combining multiple patterns.
Comments
Post a Comment