Wednesday, 25 June 2025

Design patterns in java

Design patterns are standard solutions to common software design problems.
They are like templates you can use in different situations to write flexible, reusable, and maintainable code.

Java design patterns are categorized into three main types:

📌 Summary Diagram

Design Patterns

├── Creational (object creation mechanisms)

│   ├── Singleton

│   ├── Factory Method

│   ├── Abstract Factory

│   ├── Builder

│   └── Prototype

├── Structural (Class and object composition)

│   ├── Adapter

│   ├── Bridge

│   ├── Composite

│   ├── Decorator

│   ├── Facade

│   ├── Flyweight

│   └── Proxy

└── Behavioral (Communication between objects)

    ├── Chain of Responsibility

    ├── Command

    ├── Interpreter

    ├── Iterator

    ├── Mediator

    ├── Memento

    ├── Observer

    ├── State

    ├── Strategy

    ├── Template Method

    └── Visitor

 ✅ 1. Creational Design Patterns

These patterns deal with object creation mechanisms, trying to create objects in a manner suitable to the situation.


🔹 a. Singleton Pattern

Definition: Ensures a class has only one instance and provides a global point of access to it.

Use When: You need to control access to shared resources (e.g., configuration, logging).

public class Singleton { private static Singleton instance; private Singleton() {} // private constructor public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }

🔹 b. Factory Pattern

Definition: The Factory Pattern is a creational design pattern that provides a way to create objects without exposing the creation logic to the client. Instead of using new, you call a factory method to get an object.

Use When: You have a superclass with multiple subclasses and need to return one of the subclasses based on input.

interface Shape { void draw(); } class Circle implements Shape { public void draw() { System.out.println("Circle drawn"); } } class Square implements Shape { public void draw() { System.out.println("Square drawn"); } } class ShapeFactory { public static Shape getShape(String type) { return switch (type.toLowerCase()) { case "circle" -> new Circle(); case "square" -> new Square(); default -> null; }; } } // Usage Shape shape = ShapeFactory.getShape("circle"); shape.draw();

🔹 c. Abstract Factory Pattern

Definition: Factory of factories. It provides an interface for creating families of related objects without specifying concrete classes.

Use When: You want to create related objects without knowing their concrete classes.

interface GUIFactory { Button createButton(); } interface Button { void paint(); } class WinButton implements Button { public void paint() { System.out.println("Windows Button"); } } class MacButton implements Button { public void paint() { System.out.println("Mac Button"); } } class WinFactory implements GUIFactory { public Button createButton() { return new WinButton(); } } class MacFactory implements GUIFactory { public Button createButton() { return new MacButton(); } }

🔹 d. Builder Pattern

Definition: The Builder Pattern is a creational design pattern used to construct complex objects step by step, especially when an object has many optional parameters. It helps create immutable objects with better readability and flexibility.

Use When: You want to construct complex objects in a readable and controlled manner.

class Product { private String partA; private String partB; public static class Builder { private final Product product = new Product(); public Builder setPartA(String partA) { product.partA = partA; return this; } public Builder setPartB(String partB) { product.partB = partB; return this; } public Product build() { return product; } } } // Usage Product p = new Product.Builder() .setPartA("CPU") .setPartB("RAM") .build();

🔹 e. Prototype Pattern

Definition: The Prototype Pattern is a creational design pattern that lets you create a copy (clone) of an existing object, instead of creating a new one from scratch. 

Use When: Object creation is costly.

class Prototype implements Cloneable { int id; public Prototype(int id) { this.id = id; } public Prototype clone() throws CloneNotSupportedException { return (Prototype) super.clone(); } } // Usage Prototype p1 = new Prototype(10); Prototype p2 = p1.clone();

✅ 2. Structural Design Patterns

These patterns deal with object composition, helping ensure classes work together properly.


🔹 a. Adapter Pattern

Definition: The Adapter Pattern is a structural design pattern that allows incompatible interfaces to work together. It acts as a bridge between two different interfaces by converting one into another.

Use When: You want to use an existing class but its interface does not match your requirements.

java
interface MediaPlayer { void play(String audioType, String fileName); } class MP3Player implements MediaPlayer { public void play(String audioType, String fileName) { if (audioType.equalsIgnoreCase("mp3")) System.out.println("Playing MP3: " + fileName); } } class VLCPlayer { void playVLC(String fileName) { System.out.println("Playing VLC: " + fileName); } } class MediaAdapter implements MediaPlayer { VLCPlayer vlcPlayer = new VLCPlayer(); public void play(String audioType, String fileName) { if (audioType.equalsIgnoreCase("vlc")) vlcPlayer.playVLC(fileName); } } // Usage MediaPlayer player = new MediaAdapter(); player.play("vlc", "movie.vlc");

🔹 b. Decorator Pattern

Definition: The Decorator Pattern is a structural design pattern that allows you to add new behavior or functionality to an object dynamically, without changing its structure or code.

Use When: You want to add new functionality without altering existing code.

Follows Open/Closed Principle: Open for extension, closed for modification.

java

interface Car { void assemble(); } class BasicCar implements Car { public void assemble() { System.out.print("Basic Car"); } } class SportsCar implements Car { private final Car car; public SportsCar(Car car) { this.car = car; } public void assemble() { car.assemble(); System.out.print(" + Sports Features"); } } // Usage Car car = new SportsCar(new BasicCar()); car.assemble(); // Output: Basic Car + Sports Features

🔹 c. Facade Pattern

Definition: The Facade Pattern is a structural design pattern that provides a simplified interface to a complex system of classes, libraries, or frameworks. It hides the complexities and exposes only what is necessary to the client.

java

class CPU { void start() { System.out.println("CPU Started"); } } class Memory { void load() { System.out.println("Memory Loaded"); } } class ComputerFacade { private final CPU cpu = new CPU(); private final Memory memory = new Memory(); public void startComputer() { cpu.start(); memory.load(); } } // Usage ComputerFacade computer = new ComputerFacade(); computer.startComputer();

✅ 3. Behavioral Design Patterns

These focus on communication between objects.


🔹 a. Observer Pattern

Definition: The Observer Pattern is a behavioral design pattern where an object (called Subject) maintains a list of observers and notifies them automatically of any state changes. One-to-many dependency between objects. If one changes, all dependents are notified.

Use When: Implement event handling systems.

java
interface Observer { void update(String msg); } class Subscriber implements Observer { private final String name; Subscriber(String name) { this.name = name; } public void update(String msg) { System.out.println(name + " received: " + msg); } } class Publisher { List<Observer> observers = new ArrayList<>(); void subscribe(Observer o) { observers.add(o); } void notifyAllObservers(String msg) { for (Observer o : observers) o.update(msg); } } // Usage Publisher pub = new Publisher(); pub.subscribe(new Subscriber("A")); pub.subscribe(new Subscriber("B")); pub.notifyAllObservers("Hello");

🔹 b. Strategy Pattern

Definition: The Strategy Pattern is a behavioral design pattern that allows you to define a family of algorithms, put each in a separate class, and switch between them at runtime without changing the client code.

Use When: You want to change behavior dynamically.

java
interface PaymentStrategy { void pay(int amount); } class CreditCardPayment implements PaymentStrategy { public void pay(int amount) { System.out.println("Paid with credit card: " + amount); } } class PaypalPayment implements PaymentStrategy { public void pay(int amount) { System.out.println("Paid with PayPal: " + amount); } } class Context { private PaymentStrategy strategy; Context(PaymentStrategy strategy) { this.strategy = strategy; } void pay(int amount) { strategy.pay(amount); } } // Usage Context context = new Context(new CreditCardPayment()); context.pay(500);

🔹 c. Command Pattern

Definition: Encapsulate a request as an object.

The Command Pattern is a behavioral design pattern that turns a request into an object, allowing you to parameterize methods with different requests, queue them, or log them. It also supports undo/redo operations.

java
interface Command { void execute(); } class Light { void on() { System.out.println("Light ON"); } void off() { System.out.println("Light OFF"); } } class LightOnCommand implements Command { private final Light light; LightOnCommand(Light light) { this.light = light; } public void execute() { light.on(); } } class RemoteControl { Command command; void setCommand(Command command) { this.command = command; } void pressButton() { command.execute(); } } // Usage Light light = new Light(); Command command = new LightOnCommand(light); RemoteControl remote = new RemoteControl(); remote.setCommand(command); remote.pressButton(); // Output: Light ON

 4. Microservices-Specific Design Patterns

(a a) API Gateway Pattern

 U  Used in: Centralizing requests in microservices architecture (Spring Cloud Gateway)
 @ Configuration
 p public class ApiGatewayConfig {
    @Bean
    public RouteLocator routes(RouteLocatorBuilder builder) {
        return builder.routes()
            .route("user-service", r -> r.path("/users/**").uri("lb://USER-SERVICE"))
            .route("order-service", r -> r.path("/orders/**").uri("lb://ORDER-SERVICE"))
            .build();
    }
 }
 @ Configuration
 p public class ApiGatewayConfig {
    @Bean
    public RouteLocator routes(RouteLocatorBuilder builder) {
        return builder.routes()
            .route("user-service", r -> r.path("/users/**").uri("lb://USER-SERVICE"))
            .route("order-service", r -> r.path("/orders/**").uri("lb://ORDER-SERVICE"))
            .build();
    }
 }
   b) Circuit Breaker Pattern
 U Used in: Fault tolerance (Resilience4j, Hystrix)
 @RestController
 p public class PaymentController {
    @Autowired
    private PaymentService paymentService;

    @GetMapping("/pay")
    @CircuitBreaker(name = "paymentService", fallbackMethod = "fallbackPayment")
    public String processPayment() {
        return paymentService.pay();
    }

    public String fallbackPayment(Exception e) {
        return "Payment Service is currently unavailable.";
    }
   }

C  Conclusion

In   my Spring Boot project, these design patterns help improve:

  • Scalability (e.g., Microservices patterns like API Gateway, Circuit Breaker)
  • Code reusability (e.g., Template Method, Strategy Pattern)
  • Maintainability (e.g., Singleton, Factory, Proxy)
  • Extensibility (e.g., Observer, Decorator)

✅ Summary Table

PatternCategoryPurpose
SingletonCreationalOne instance, global access
FactoryCreationalCreates object based on input
Abstract FactoryCreationalCreates related families of objects
BuilderCreationalStep-by-step object construction
PrototypeCreationalClone existing object
AdapterStructuralConverts one interface to another
DecoratorStructuralAdds behavior dynamically
FacadeStructuralSimplifies subsystem access
ObserverBehavioralPublish-subscribe model
StrategyBehavioralChoose algorithm at runtime
CommandBehavioralEncapsulate actions as objects

No comments:

Post a Comment