Showing posts with label Design Pattern. Show all posts
Showing posts with label Design Pattern. Show all posts

Java Behavioral Desigh Patterns

 

🟣 BEHAVIORAL PATTERNS

(How objects interact, communicate, and change behavior)

Behavioral patterns focus on:

  • Communication between objects

  • Delegation of responsibilities

  • Runtime behavior changes

They help reduce tight coupling and make workflows extensible.


🔟 Observer Pattern

What

Observer defines a one-to-many dependency between objects.
When the Subject changes state, all Observers are notified automatically.

Why

Without Observer:

  • Subject must directly call every dependent class

  • Tight coupling between publisher and subscribers

  • Adding a new listener requires modifying subject code

Observer promotes event-driven design.

When

Use Observer when:

  • Multiple objects depend on one object’s state

  • You want publish-subscribe behavior

Real-world examples:

  • UI event listeners

  • Stock price notifications

  • Kafka consumers

Full Java Program (with Client)

import java.util.*; interface Observer { void update(String message); } class Subscriber implements Observer { private final String name; Subscriber(String name) { this.name = name; } public void update(String message) { System.out.println(name + " received: " + message); } } class NewsAgency { private final List<Observer> observers = new ArrayList<>(); void subscribe(Observer o) { observers.add(o); } void publish(String news) { for (Observer o : observers) { o.update(news); } } } public class ObserverClient { public static void main(String[] args) { NewsAgency agency = new NewsAgency(); agency.subscribe(new Subscriber("Vinod")); agency.subscribe(new Subscriber("John")); agency.publish("Java 21 Released"); } }

1️⃣1️⃣ Strategy Pattern

What

Strategy defines a family of algorithms, encapsulates each one, and makes them interchangeable at runtime.

Why

Without Strategy:

  • Large if-else or switch blocks

  • Adding a new algorithm requires modifying existing logic

  • Violates Open–Closed Principle

When

Use Strategy when:

  • Multiple ways exist to perform an operation

  • Algorithm must change at runtime

Real-world examples:

  • Payment methods

  • Compression algorithms

  • Sorting strategies

Full Java Program (with Client)

interface PaymentStrategy { void pay(int amount); } class CardPayment implements PaymentStrategy { public void pay(int amount) { System.out.println("Paid " + amount + " using CARD"); } } class UpiPayment implements PaymentStrategy { public void pay(int amount) { System.out.println("Paid " + amount + " using UPI"); } } class Checkout { private PaymentStrategy strategy; Checkout(PaymentStrategy strategy) { this.strategy = strategy; } void setStrategy(PaymentStrategy strategy) { this.strategy = strategy; } void pay(int amount) { strategy.pay(amount); } } public class StrategyClient { public static void main(String[] args) { Checkout checkout = new Checkout(new CardPayment()); checkout.pay(1000); checkout.setStrategy(new UpiPayment()); checkout.pay(500); } }

1️⃣2️⃣ Command Pattern

What

Command encapsulates a request as an object, allowing it to be queued, logged, or undone.

Why

Without Command:

  • Sender tightly coupled to receiver

  • No easy undo/redo

  • Hard to queue or schedule operations

When

Use Command when:

  • You need undo/redo functionality

  • UI actions trigger business logic

  • Operations must be queued or logged

Real-world examples:

  • Button clicks

  • Job queues

  • Transaction commands

Full Java Program (with Client)

interface Command { void execute(); } class Light { void on() { System.out.println("Light ON"); } } class LightOnCommand implements Command { private final Light light; LightOnCommand(Light light) { this.light = light; } public void execute() { light.on(); } } class RemoteControl { private Command command; void setCommand(Command command) { this.command = command; } void pressButton() { command.execute(); } } public class CommandClient { public static void main(String[] args) { Light light = new Light(); RemoteControl remote = new RemoteControl(); remote.setCommand(new LightOnCommand(light)); remote.pressButton(); } }

1️⃣3️⃣ State Pattern

What

State allows an object to change its behavior when its internal state changes, without if-else logic.

Why

Without State:

  • Large conditional blocks

  • Hard to add new states

  • Violates Open–Closed Principle

When

Use State when:

  • Object behavior depends on state

  • State transitions are well defined

Real-world examples:

  • Order lifecycle

  • ATM states

  • Traffic lights

Full Java Program (with Client)

interface State { void next(Context ctx); } class Context { private State state; Context(State state) { this.state = state; } void setState(State state) { this.state = state; } void next() { state.next(this); } } class NewState implements State { public void next(Context ctx) { System.out.println("Order Paid"); ctx.setState(new PaidState()); } } class PaidState implements State { public void next(Context ctx) { System.out.println("Order Shipped"); } } public class StateClient { public static void main(String[] args) { Context order = new Context(new NewState()); order.next(); order.next(); } }

1️⃣4️⃣ Chain of Responsibility Pattern

What

Chain of Responsibility passes a request through a chain of handlers until one handles it.

Why

Avoids:

  • Large if-else chains

  • Tight coupling between sender and receiver

When

Use Chain when:

  • Multiple handlers may process a request

  • Order of processing matters

Real-world examples:

  • Authentication filters

  • Logging pipelines

  • Validation chains

Full Java Program (with Client)

abstract class Handler { protected Handler next; Handler setNext(Handler next) { this.next = next; return next; } abstract void handle(); } class AuthHandler extends Handler { void handle() { System.out.println("Authentication passed"); if (next != null) next.handle(); } } class LogHandler extends Handler { void handle() { System.out.println("Request logged"); } } public class ChainClient { public static void main(String[] args) { new AuthHandler() .setNext(new LogHandler()) .handle(); } }

1️⃣5️⃣ Mediator Pattern

What

Mediator centralizes communication between objects so they don’t talk to each other directly.

Why

Without Mediator:

  • Objects become tightly coupled

  • Changes ripple across classes

When

Use Mediator when:

  • Many objects interact

  • Communication logic is complex

Real-world examples:

  • Chat rooms

  • UI component interaction

  • Air traffic control

Full Java Program (with Client)

class Mediator { void send(String message) { System.out.println(message); } } class User { private final String name; private final Mediator mediator; User(String name, Mediator mediator) { this.name = name; this.mediator = mediator; } void send(String msg) { mediator.send(name + ": " + msg); } } public class MediatorClient { public static void main(String[] args) { Mediator mediator = new Mediator(); new User("Vinod", mediator).send("Hello"); } }

1️⃣6️⃣ Memento Pattern

What

Memento captures and stores an object’s internal state so it can be restored later.

Why

Direct access to internal state breaks encapsulation.

When

Use Memento when:

  • Undo functionality is required

  • State rollback is needed

Real-world examples:

  • Text editors

  • Game save points

Full Java Program (with Client)

class Editor { private String text; static class Memento { private final String state; Memento(String state) { this.state = state; } } void setText(String text) { this.text = text; } String getText() { return text; } Memento save() { return new Memento(text); } void restore(Memento memento) { this.text = memento.state; } } public class MementoClient { public static void main(String[] args) { Editor editor = new Editor(); editor.setText("Version 1"); Editor.Memento backup = editor.save(); editor.setText("Version 2"); editor.restore(backup); System.out.println(editor.getText()); } }

1️⃣7️⃣ Iterator Pattern

What

Iterator provides a way to traverse a collection without exposing its internal structure.

Why

  • Prevents exposing internals

  • Provides uniform traversal

When

Use when:

  • Traversing collections

  • Uniform access required

Full Java Program (with Client)

import java.util.*; public class IteratorClient { public static void main(String[] args) { List<String> list = List.of("A", "B", "C"); Iterator<String> it = list.iterator(); while (it.hasNext()) { System.out.println(it.next()); } } }

1️⃣8️⃣ Interpreter Pattern

What

Defines a grammar and interprets expressions of that grammar.

Why

Simplifies expression evaluation logic.

When

Use when:

  • Simple DSL required

  • Rule engines or expression evaluators

Full Java Program (with Client)

interface Expression { int interpret(); } class NumberExpr implements Expression { int value; NumberExpr(int value) { this.value = value; } public int interpret() { return value; } } class AddExpr implements Expression { Expression left, right; AddExpr(Expression left, Expression right) { this.left = left; this.right = right; } public int interpret() { return left.interpret() + right.interpret(); } } public class InterpreterClient { public static void main(String[] args) { Expression expr = new AddExpr(new NumberExpr(5), new NumberExpr(10)); System.out.println(expr.interpret()); } }

1️⃣9️⃣ Visitor Pattern

What

Visitor allows adding new operations to object structures without modifying them.

Why

Prevents bloated domain classes.

When

Use when:

  • Object structure is stable

  • Operations change frequently

Full Java Program (with Client)

interface Visitor { void visit(Book book); } interface Item { void accept(Visitor visitor); } class Book implements Item { int price = 500; public void accept(Visitor visitor) { visitor.visit(this); } } class PriceVisitor implements Visitor { public void visit(Book book) { System.out.println("Book price: " + book.price); } } public class VisitorClient { public static void main(String[] args) { new Book().accept(new PriceVisitor()); } }

Java Design Patterns With What • Why • When • Full Java Programs • Client Usage

 

Java Design Patterns 

With What • Why • When • Full Java Programs • Client Usage


Introduction

Design Patterns are proven solutions to recurring software design problems.
They are not frameworks or libraries, but design ideas that help you write:

  • Maintainable code

  • Loosely coupled systems

  • Extensible architectures

In Java, design patterns are grouped into:

  1. Creational – How objects are created

  2. Structural – How objects are composed

  3. Behavioral – How objects interact


🟢 CREATIONAL PATTERNS


1️⃣ Singleton Pattern

What

Singleton ensures only one instance of a class exists in the entire application and provides a global access point to it.


Why

Some objects represent global shared state.
Creating multiple instances causes inconsistency.

Problems without Singleton:

  • Multiple config loaders with different values

  • Multiple loggers writing to the same file

  • Multiple cache managers


When

Use Singleton when:

  • Exactly one instance is required

  • Object is shared across the system

Real examples:

  • Configuration manager

  • Logger

  • Metrics registry


Full Java Program (with Client)

final class AppConfig { private static final AppConfig INSTANCE = new AppConfig(); private AppConfig() { // private constructor } public static AppConfig getInstance() { return INSTANCE; } public String getEnvironment() { return "PRODUCTION"; } } public class SingletonClient { public static void main(String[] args) { AppConfig c1 = AppConfig.getInstance(); AppConfig c2 = AppConfig.getInstance(); System.out.println(c1.getEnvironment()); System.out.println("Same instance? " + (c1 == c2)); } }

2️⃣ Factory Method Pattern

What

Factory Method creates objects without exposing the creation logic to the client.


Why

Direct object creation (new) tightly couples client code to concrete classes.

Problems without Factory:

  • Large if-else blocks

  • Client changes when implementation changes

  • Poor extensibility


When

Use Factory when:

  • Multiple implementations exist

  • Object type depends on input or config

Real examples:

  • Payment systems

  • Notification services

  • Parser creation


Full Java Program (with Client)

interface Payment { void pay(double amount); } class UpiPayment implements Payment { public void pay(double amount) { System.out.println("Paid " + amount + " using UPI"); } } class CardPayment implements Payment { public void pay(double amount) { System.out.println("Paid " + amount + " using Card"); } } class PaymentFactory { public static Payment create(String type) { if ("UPI".equalsIgnoreCase(type)) { return new UpiPayment(); } if ("CARD".equalsIgnoreCase(type)) { return new CardPayment(); } throw new IllegalArgumentException("Invalid payment type"); } } public class FactoryClient { public static void main(String[] args) { Payment payment = PaymentFactory.create("UPI"); payment.pay(1000); } }

3️⃣ Abstract Factory Pattern

What

Abstract Factory creates families of related objects that must work together.


Why

Creating related objects independently can cause incompatibility.

Example problem:

  • DarkButton + LightCheckbox ❌

  • AWSCompute + AzureStorage ❌


When

Use Abstract Factory when:

  • Objects must be used together

  • Multiple product families exist

Real examples:

  • UI themes

  • Cloud providers

  • OS-specific components


Full Java Program (with Client)

interface Button { void render(); } class DarkButton implements Button { public void render() { System.out.println("Rendering Dark Button"); } } class LightButton implements Button { public void render() { System.out.println("Rendering Light Button"); } } interface UIFactory { Button createButton(); } class DarkUIFactory implements UIFactory { public Button createButton() { return new DarkButton(); } } class LightUIFactory implements UIFactory { public Button createButton() { return new LightButton(); } } public class AbstractFactoryClient { public static void main(String[] args) { UIFactory factory = new DarkUIFactory(); Button button = factory.createButton(); button.render(); } }

4️⃣ Builder Pattern

What

Builder constructs complex objects step-by-step, separating construction from representation.


Why

Constructors with many parameters are:

  • Hard to read

  • Error-prone

  • Difficult to maintain


When

Use Builder when:

  • Object has many optional fields

  • Immutability is required

Real examples:

  • DTOs

  • API request objects

  • Configuration objects


Full Java Program (with Client)

class User { private final String name; private final int age; private final String city; private User(Builder builder) { this.name = builder.name; this.age = builder.age; this.city = builder.city; } static class Builder { private String name; private int age; private String city; Builder name(String name) { this.name = name; return this; } Builder age(int age) { this.age = age; return this; } Builder city(String city) { this.city = city; return this; } User build() { return new User(this); } } public String toString() { return name + ", " + age + ", " + city; } } public class BuilderClient { public static void main(String[] args) { User user = new User.Builder() .name("Vinod") .age(35) .city("Cupertino") .build(); System.out.println(user); } }

5️⃣ Prototype Pattern

What

Prototype creates new objects by cloning an existing object.


Why

Some objects are expensive to create.
Cloning is faster than rebuilding.


When

Use Prototype when:

  • Many similar objects are needed

  • Initialization is costly

Real examples:

  • Report templates

  • Game objects


Full Java Program (with Client)

class Report implements Cloneable { String title; Report(String title) { this.title = title; } public Report clone() { try { return (Report) super.clone(); } catch (Exception e) { throw new RuntimeException(e); } } } public class PrototypeClient { public static void main(String[] args) { Report template = new Report("Monthly Report"); Report jan = template.clone(); jan.title = "January Report"; System.out.println(jan.title); } }

🔵 STRUCTURAL PATTERNS


6️⃣ Adapter Pattern

What

Adapter converts one interface into another that the client expects.


Why

Legacy or third-party code cannot be modified, but your system expects a different interface.


When

Use Adapter when:

  • Integrating legacy systems

  • Using third-party SDKs

  • Interfaces don’t match


Full Java Program (with Client)

class LegacyLogger { void writeLog(String msg) { System.out.println("Legacy: " + msg); } } interface Logger { void log(String msg); } class LoggerAdapter implements Logger { private final LegacyLogger legacyLogger; LoggerAdapter(LegacyLogger legacyLogger) { this.legacyLogger = legacyLogger; } public void log(String msg) { legacyLogger.writeLog(msg); } } public class AdapterClient { public static void main(String[] args) { Logger logger = new LoggerAdapter(new LegacyLogger()); logger.log("Application started"); } }

7️⃣ Decorator Pattern

What

Decorator adds new behavior dynamically without modifying the original class.


Why

Subclassing leads to class explosion.
Decorator uses composition instead.


When

Use Decorator when:

  • Behavior is optional

  • Multiple features must be combined

Real examples:

  • Logging

  • Security

  • Metrics


Full Java Program (with Client)

interface Service { String execute(); } class CoreService implements Service { public String execute() { return "Core Service"; } } class LoggingDecorator implements Service { private final Service service; LoggingDecorator(Service service) { this.service = service; } public String execute() { System.out.println("Before execution"); String result = service.execute(); System.out.println("After execution"); return result; } } public class DecoratorClient { public static void main(String[] args) { Service service = new LoggingDecorator(new CoreService()); service.execute(); } }

8️⃣ Facade Pattern

What

Facade provides a simple interface to a complex subsystem.


Why

Subsystems are difficult to use directly.


When

Use Facade when:

  • System has many internal classes

  • Client needs a simple API


Full Java Program (with Client)

class Video { void play() { System.out.println("Playing video"); } } class Audio { void play() { System.out.println("Playing audio"); } } class MediaFacade { private final Video video = new Video(); private final Audio audio = new Audio(); void play() { video.play(); audio.play(); } } public class FacadeClient { public static void main(String[] args) { new MediaFacade().play(); } }

9️⃣ Proxy Pattern

What

Proxy controls access to another object.


Why

Direct access may be expensive or insecure.


When

Use Proxy when:

  • Lazy loading is needed

  • Access control is required


Full Java Program (with Client)

interface Image { void display(); } class RealImage implements Image { RealImage() { System.out.println("Loading image from disk..."); } public void display() { System.out.println("Displaying image"); } } class ImageProxy implements Image { private RealImage realImage; public void display() { if (realImage == null) { realImage = new RealImage(); } realImage.display(); } } public class ProxyClient { public static void main(String[] args) { Image image = new ImageProxy(); image.display(); } }
 
 

Confusion Matrix + Precision/Recall (Super Simple, With Examples)

  Confusion Matrix + Precision/Recall (Super Simple, With Examples) 1) Binary Classification Setup Binary classification means the model p...

Featured Posts