Wednesday, 25 June 2025

Iterator Design Pattern

 

                                                                  Iterator Design Pattern

The Iterator Design Pattern is one of the behavioral design patterns. It provides a standard way to traverse or iterate through a collection of objects (like lists, trees, or other data structures) without exposing the underlying representation of the collection.

Intent

  • To access elements of a collection sequentially without exposing its underlying representation.

Key Components

  1. Iterator (Interface)
    • Defines the interface for accessing and traversing elements.
    • Example methods: hasNext(), next()
  2. Concrete Iterator
    • Implements the Iterator interface.
    • Keeps track of the current position in the traversal.
  3. Aggregate / Collection (Interface)
    • Defines the interface for creating an iterator object.
  4. Concrete Aggregate
    • Implements the Aggregate interface.
    • Returns an instance of the concrete iterator.

📘 Advantages

  • Hides the internal structure of collections.
  • Provides a uniform interface for traversing different collection types.
  • Supports multiple simultaneous iterations on the same collection.

⚠️ Disadvantages

  • Adds complexity in custom collections.
  • Might lead to overhead if used in simple structures.

Java Built-in Support

Java provides built-in support for this pattern through the Iterator interface found in the java.util package.

Iterator<String> iterator = list.iterator();

while (iterator.hasNext()) {

    String element = iterator.next();

    System.out.println(element);

}

 

 

Example :

Step 1: Create the Iterator interface

package iteratordesignpattern;

 

public interface Iterator {

               boolean hasNext(); 

    Object next();

}

 

Step 2: Create the Container interface

 

package iteratordesignpattern;

 

public interface Container {

 

               Iterator getItertaor();

}

 

 

 

Step 3: Create NameRepository class

package iteratordesignpattern;

public class NameRepository implements Container {

               public String[] names = { "Govind", "Ballabh", "Khan", "Java", "Technology" };

 

               @Override

               public Iterator getItertaor() {

                              // TODO Auto-generated method stub

                               return new NameIterator();

               }

               private class NameIterator implements Iterator {

                              int index;

 

                              @Override

                              public boolean hasNext() {

                                             // TODO Auto-generated method stub

                                             return index < names.length;

                              }

 

                              @Override

                              public Object next() {

                                             if (this.hasNext()) {

                                                            return names[index++];

                                             }

                                             return null;

                              }

 

               }

 

}

 

Step 4: Test the Iterator

package iteratordesignpattern;

 

public class IteratorPatternDemo {

               public static void main(String[] args) {

                              NameRepository nameRepository = new NameRepository();

 

                              for (Iterator iterator = nameRepository.getItertaor(); iterator.hasNext();) {

          String name = (String)iterator.next();

          System.out.println(name);

                              }

               }

}

 

 

o/p:

Govind

Ballabh

Khan

Java

Technology

 

Example :2

 

package iteratordesignpattern;

 

public class Book {

 

               private String title;

               private String author;

 

               public Book(String title, String author) {

                              super();

                              this.title = title;

                              this.author = author;

               }

 

               public String getTitle() {

                              return title;

               }

 

               public void setTitle(String title) {

                              this.title = title;

               }

 

               public String getAuthor() {

                              return author;

               }

 

               public void setAuthor(String author) {

                              this.author = author;

               }

 

}

 

 

 

 

 

package iteratordesignpattern;

 

public interface Iterator {

               boolean hasNext(); 

    Object next();

}

 

 

 

package iteratordesignpattern;

 

public interface Container {

 

               Iterator getItertaor();

}

 

 

 

 

package iteratordesignpattern;

 

public class BookRepository implements Container {

 

               private int count = 0;

               private Book[] books;

 

               public BookRepository(int size) {

                              books = new Book[size];

               }

 

               public void addBook(Book book) {

                              if (count < books.length) {

                                             books[count] = book;

                                             count++;

                              }

               }

 

               @Override

               public Iterator getItertaor() {

                              return new BookIterator();

 

               }

 

               public class BookIterator implements Iterator {

                              private int index = 0;

 

                              @Override

                              public boolean hasNext() {

                                             // TODO Auto-generated method stub

                                             return index < count && books[index] != null;

                              }

 

                              @Override

                              public Object next() {

                                             if (this.hasNext()) {

                                                            return books[index++];

                                             }

                                             return null;

                              }

 

               }

 

}

 

 

package iteratordesignpattern;

 

public class BookIteratorDemo {

 

               public static void main(String[] args) {

                              BookRepository bookRepository = new BookRepository(5);

                              bookRepository.addBook(new Book("OS", "Galvin"));

                              bookRepository.addBook(new Book("Java", "Durga Sir"));

                              bookRepository.addBook(new Book("C++", "Yashwantkanetkar"));

 

                              Iterator iterator = bookRepository.getItertaor();

 

                              while (iterator.hasNext()) {

                                             Book book = (Book) iterator.next();

                                             System.out.println("Book: " + book.getTitle() + ", Author: " + book.getAuthor());

                              }

               }

}

 

 

 

o/p:

 

Book: OS, Author: Galvin

Book: Java, Author: Durga Sir

Book: C++, Author: Yashwantkanetkar

 

 

 

 

Friday, 20 June 2025

 

Decorator Pattern

The Decorator Pattern is a structural design pattern that lets you attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.

In Java, the Decorator Pattern is used to wrap objects in decorator classes that add new behaviors or responsibilities without changing the original object's code.

 

Intent

  • Attach additional responsibilities to an object dynamically.
  • Provide a flexible alternative to subclassing for extending functionality.
  • Keep the core object and its extensions separate.
  • Enable adding features transparently to clients.

Participants

The main participants in the Decorator Pattern are:

a. Component (interface or abstract class)

  • Defines the interface for objects that can have responsibilities added to them dynamically.
  • Example: Text interface with getContent() method.

b. ConcreteComponent (implements Component)

  • The core object to which additional responsibilities can be attached.
  • Example: SimpleText class that implements Text.

c. Decorator (abstract class or interface implementing Component)

  • Maintains a reference to a Component object.
  • Implements the Component interface.
  • Delegates operations to the wrapped component.
  • Acts as a base class for concrete decorators.

d. ConcreteDecorator (extends Decorator)

  • Adds responsibilities to the component.
  • Overrides component methods to provide extra behavior.
  • Example: UpperCaseDecorator and StarDecorator.

 

Uml

 

 

How it Works

  • The client interacts with objects through the Component interface.
  • A ConcreteComponent is created, which implements the basic behavior.
  • To add functionality, a Decorator wraps the ConcreteComponent.
  • Multiple decorators can wrap the same component to combine behaviors.
  • Decorators forward requests to the wrapped component, adding extra processing before or after forwarding.

Benefits (Advantages)

  • Open/Closed Principle: You can add new behavior without modifying existing code.
  • Flexible extension: New functionality can be added at runtime.
  • Avoids an explosion of subclasses by combining decorators instead of subclassing.
  • Can wrap multiple decorators to compose complex behavior.
  • Transparent to the client — clients use decorated objects via the same interface.

Drawbacks (Disadvantages)

  • Can produce a large number of small classes.
  • The design can become complex if many decorators are stacked.
  • Debugging can be harder because of many layers of wrapping.
  • Slight performance overhead due to multiple levels of indirection.

Typical Use Cases in Java

  • Adding features to streams (java.io package uses Decorator pattern extensively: BufferedReader, InputStreamReader).
  • GUI frameworks for adding borders, scrollbars, or behaviors to components.
  • Adding responsibilities such as logging, caching, validation dynamically.
  • Adding filters or transformations to data streams or collections.

 

Real-world Example (Java IO)

Java’s IO library is a classic example of the Decorator Pattern:

  • InputStream is the Component.
  • FileInputStream is a ConcreteComponent.
  • BufferedInputStream is a Decorator adding buffering.
  • DataInputStream is another Decorator adding data-type reading.

You can wrap a FileInputStream inside a BufferedInputStream and then wrap that in a DataInputStream to combine functionalities dynamically.

InputStream file = new FileInputStream("file.txt");

InputStream buffer = new BufferedInputStream(file);

DataInputStream data = new DataInputStream(buffer);

 

int value = data.readInt();

Summary

Aspect

Description

Pattern Type

Structural

Purpose

Add responsibilities dynamically

Key Principle

Composition over inheritance

Key Benefit

Flexible, reusable, open to extension

Main Components

Component, ConcreteComponent, Decorator, ConcreteDecorator

Common Usage

Java IO, GUI component decoration

 

 

 

Example : -

Component

package decoratordesignpattern;

 

public interface Text {

               String getContent();

}

 

ConcreteComponent

 

 

package decoratordesignpattern;

 

public class SimpleText implements Text {

 

               private String content;

 

               public SimpleText(String content) {

                              super();

                              this.content = content;

               }

 

               @Override

               public String getContent() {

                              return content;

               }

 

}

 

 

Decorator

 

package decoratordesignpattern;

 

public abstract class TextDecorator implements Text {

 

               protected Text decoratedText;

 

               public TextDecorator(Text decoratedText) {

                              super();

                              this.decoratedText = decoratedText;

               }

 

               @Override

               public String getContent() {

                              return decoratedText.getContent();

               }

}

 

 

 

ConcreteDecorator

 

package decoratordesignpattern;

 

public class UpperCaseDecorator extends TextDecorator {

 

               public UpperCaseDecorator(Text decoratedText) {

                              super(decoratedText);

 

               }

 

               public String getContent() {

                              return decoratedText.getContent().toUpperCase();

               }

 

}

 

 

package decoratordesignpattern;

 

public class StarDecorator extends TextDecorator {

 

               public StarDecorator(Text decoratedText) {

                              super(decoratedText);

                              // TODO Auto-generated constructor stub

               }

 

               @Override

               public String getContent() {

                              return "*** " + decoratedText.getContent() + " ***";

               }

 

}

 

 

 

package decoratordesignpattern;

 

public class DecoratorDemo {

 

               public static void main(String[] args) {

 

                              Text myText = new SimpleText("Hello World!");

 

                              // Wrap with uppercase decorator

                              Text upper = new UpperCaseDecorator(myText);

                              System.out.println(upper.getContent()); // Output: HELLO, WORLD!

 

                              // Wrap with star decorator

                              Text starred = new StarDecorator(myText);

                              System.out.println(starred.getContent()); // Output: *** Hello, world! ***

 

                              // Combine decorators (starred + uppercase)

                              Text starredUpper = new StarDecorator(upper);

                              System.out.println(starredUpper.getContent()); // Output: *** HELLO, WORLD! ***

 

               }

}

 

 

 

o/p :-

HELLO WORLD!

*** Hello World! ***

*** HELLO WORLD! ***

 

 

 

 

 

 

 

 

 

 

 

 

 

Strategy Design Pattern

 

                                                           Strategy Design Pattern

The Strategy Design Pattern is a behavioral pattern that allows you to define a family of algorithms, put each of them in a separate class, and make them interchangeable at runtime.

Use Case: Payment Strategy

Example Scenario: Payment System

Imagine an e-commerce app where customers can choose how to pay:

  • Credit card
  • PayPal
  • UPI

Instead of hardcoding multiple if-else or switch statements, use Strategy Pattern to plug in different payment behaviors.

This keeps code flexible, maintainable, and scalable.

Key Components

  1. Strategy Interface
    • Declares a common method that all concrete strategies must implement.
  2. Concrete Strategies
    • Implement different versions of the algorithm.
  3. Context
    • Uses a strategy object and delegates it the task of executing the algorithm.

UML Diagram

 

 

Example

Strategy Interface

package stratgydesignpattern;

 

public interface PaymentStrategy {

void pay(double amount);

}

 

Concrete Strategy Classes

 

package stratgydesignpattern;

 

public class CreditCardPayment implements PaymentStrategy {

 

               private String cardNumber;

               private String cardHolder;

 

               public CreditCardPayment(String cardNumber, String cardHolder) {

                              super();

                              this.cardNumber = cardNumber;

                              this.cardHolder = cardHolder;

               }

 

               @Override

               public void pay(double amount) {

                              System.out.println("Paid ₹" + amount + " using Credit Card.");

 

               }

 

}

 

 

package stratgydesignpattern;

 

public class PayPalPayment implements PaymentStrategy {

 

               private String email;

 

               public PayPalPayment(String email) {

                              super();

                              this.email = email;

               }

 

               @Override

               public void pay(double amount) {

                              System.out.println("Paid ₹" + amount + " using PayPal.");

 

               }

 

}

 

 

package stratgydesignpattern;

 

public class UpiPayment implements PaymentStrategy {

 

               private String upiId;

 

               public UpiPayment(String upiId) {

                              this.upiId = upiId;

               }

 

               @Override

               public void pay(double amount) {

                              System.out.println("Paid ₹" + amount + " using UPI.");

 

               }

}

 

 

Context Class

 

package stratgydesignpattern;

 

public class PaymentContext {

 

               private PaymentStrategy paymentStrategy;

 

               public void setPaymentStrategy(PaymentStrategy paymentStrategy) {

                              this.paymentStrategy = paymentStrategy;

               }

 

               public void payAmount(double amount) {

                              if (paymentStrategy == null) {

                                             System.out.println("Payment strategy not set!");

                                             return;

                              }

 

                              paymentStrategy.pay(amount);

               }

 

}

 

Client Code

package stratgydesignpattern;

 

public class Cient {

 

               public static void main(String[] args) {

 

                              PaymentContext paymentContext = new PaymentContext();

 

                              paymentContext.setPaymentStrategy(new CreditCardPayment("123456789", "Govind Khan"));

 

                              paymentContext.payAmount(1000);

 

                              paymentContext.setPaymentStrategy(new PayPalPayment("govindkhan@gmail.com"));

                              paymentContext.payAmount(800);

 

                              paymentContext.setPaymentStrategy(new UpiPayment("govind@upi"));

                              paymentContext.payAmount(500);

               }

 

}

 

o/p :-

 

Paid ₹1000.0 using Credit Card.

Paid ₹800.0 using PayPal.

Paid ₹500.0 using UPI.

 

 

 

Use Cases in Java (Real-World Examples)

1. Sorting Algorithms in Java

Java’s Collections.sort() method uses Strategy Pattern internally.

List<Employee> employees = new ArrayList<>();

Collections.sort(employees, new SalaryComparator());

  • Comparator<T> is the strategy interface
  • SalaryComparator, NameComparator are concrete strategies

2. Java Streams

list.stream().sorted(Comparator.comparing(Employee::getName));

  • Comparator is a strategy
  • You can pass different sorting strategies dynamically

3. Java Swing / GUI Frameworks

Swing uses Strategy pattern in layout managers.

JPanel panel = new JPanel();

panel.setLayout(new FlowLayout()); // or new GridLayout()

Each layout (Flow, Grid, Border) is a different strategy for arranging UI components.


4. Spring Framework

  • Spring Security uses Strategy pattern for authentication and authorization mechanisms.
  • Spring Boot uses strategy pattern for logging, error handling, and data serialization by letting you plug in different implementations.

Example:

AuthenticationManager authManager = new ProviderManager(List.of(new DaoAuthenticationProvider()));


🔧 Benefits of Strategy Pattern

Benefit

Description

Open/Closed Principle

Add new strategies without modifying existing code.

Decoupling

Decouple behavior from context (class using it).

Runtime Flexibility

Switch algorithms at runtime.


⚠️ When Not to Use

  • If the behavior doesn’t change or is unlikely to vary.
  • When adding too many strategies would make the code more complex than necessary.

🔚 Summary

  • Strategy Pattern is perfect when you have multiple ways of doing something, like different payment options, sorting methods, or layout managers.
  • Java's standard libraries and frameworks like Collections, Streams, Swing, and Spring make heavy use of it.
  • It improves flexibility, reusability, and clean separation of concerns.