Iterator Design Pattern example
Problem Statement:
Let’s say we have a collection of employees in a company, and we want to iterate over the employees to calculate their total salary. However, the employees are stored in different types of collections (arrays, lists, etc.), and we want to iterate over them without exposing the underlying collection types.
Benefit of using the Iterator Pattern
The Iterator pattern allows us to access the elements of a collection sequentially without exposing its underlying representation. It provides a way to iterate over a collection regardless of its internal structure.
Below is the code of above problem statement using Iterator Pattern:
Let’s break down into the component wise code:
1. Iterator Interface
Defines the methods for accessing and traversing the collection.
interface Iterator<T> {
boolean hasNext();
T next();
}
2. Aggregate Interface
Defines the method for creating an iterator.
interface Aggregate<T> {
Iterator<T> createIterator();
}
3. Concrete Iterator
Implements the Iterator interface and provides the actual iteration logic.
class EmployeeIterator implements Iterator<Employee> {
private int currentIndex = 0;
private List<Employee> employees;
public EmployeeIterator(List<Employee> employees) {
this.employees = employees;
}
@Override
public boolean hasNext() {
return currentIndex < employees.size();
}
@Override
public Employee next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return employees.get(currentIndex++);
}
}
4. Concrete Aggregate
Implements the Aggregate interface and provides the method to create an iterator for the collection.
class Company implements Aggregate<Employee> {
private List<Employee> employees;
public Company(List<Employee> employees) {
this.employees = employees;
}
@Override
public Iterator<Employee> createIterator() {
return new EmployeeIterator(employees);
}
}
Complete code for the above example
This code demonstrates how the Iterator pattern can be used to iterate over a collection of employees in a company, regardless of the internal storage of the employees. Below is the complete code for the above example:
import java.util.*;
// Employee class
class Employee {
private String name;
private double salary;
public Employee(String name, double salary) {
this.name = name;
this.salary = salary;
}
public double getSalary() {
return salary;
}
}
// Iterator interface
interface Iterator<T> {
boolean hasNext();
T next();
}
// Aggregate interface
interface Aggregate<T> {
Iterator<T> createIterator();
}
// Concrete Iterator
class EmployeeIterator implements Iterator<Employee> {
private int currentIndex = 0;
private List<Employee> employees;
public EmployeeIterator(List<Employee> employees) {
this.employees = employees;
}
@Override
public boolean hasNext() {
return currentIndex < employees.size();
}
@Override
public Employee next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return employees.get(currentIndex++);
}
}
// Concrete Aggregate
class Company implements Aggregate<Employee> {
private List<Employee> employees;
public Company(List<Employee> employees) {
this.employees = employees;
}
@Override
public Iterator<Employee> createIterator() {
return new EmployeeIterator(employees);
}
}
// Main class
public class Main {
public static void main(String[] args) {
List<Employee> employees = new ArrayList<>();
employees.add(new Employee("Alice", 50000));
employees.add(new Employee("Bob", 60000));
employees.add(new Employee("Charlie", 70000));
Company company = new Company(employees);
Iterator<Employee> iterator = company.createIterator();
double totalSalary = 0;
while (iterator.hasNext()) {
totalSalary += iterator.next().getSalary();
}
System.out.println("Total salary: " + totalSalary);
}
}
class Employee {
constructor(name, salary) {
this.name = name;
this.salary = salary;
}
}
// Iterator interface
class Iterator {
constructor(collection) {
this.collection = collection;
this.currentIndex = 0;
}
hasNext() {
return this.currentIndex < this.collection.length;
}
next() {
if (!this.hasNext()) {
throw new Error('No such element');
}
return this.collection[this.currentIndex++];
}
}
// Aggregate interface
class Aggregate {
createIterator() {
return null;
}
}
// Concrete Iterator
class EmployeeIterator extends Iterator {
constructor(collection) {
super(collection);
}
}
// Concrete Aggregate
class Company extends Aggregate {
constructor(collection) {
super();
this.collection = collection;
}
createIterator() {
return new EmployeeIterator(this.collection);
}
}
// Main function
function main() {
const employees = [
new Employee("Alice", 50000),
new Employee("Bob", 60000),
new Employee("Charlie", 70000)
];
const company = new Company(employees);
const iterator = company.createIterator();
let totalSalary = 0;
while (iterator.hasNext()) {
totalSalary += iterator.next().salary;
}
console.log("Total salary: " + totalSalary);
}
main();
Total salary: 180000.0
Iterator Design Pattern
The Iterator pattern is a widely used design pattern in software development that provides a way to access the elements of an aggregate object (such as a list or collection) sequentially without exposing its underlying representation.
Table of Content
- What is the Iterator Design Pattern?
- Components of Iterator Design Pattern
- Iterator Design Pattern example
- When to use Iterator Design Pattern
- When to not use Iterator Design Pattern