Tuesday, April 25, 2017

Iterator Design Pattern in Java

INTRODUCTION

  • Iterator Design pattern is categorized under Behavioral Patterns.
  • It enables the access to all the element of an aggregate object (list of). Iteration will be performed on the collection of elements of the object sequentially without exposing the actual representation.
  • Also, enables the traversal on the element of different data structures.
  • Separating the traversal responsibility from the client code and moving to another class called Iterator. It holds the details about which elements are traversed and which elements yet to be traversed.
  • An iterator will be defined as an Interface in java and have the basic methods next() and hasNext().
    • next() method helps to get the next element in the object.
    • hasNext() method helps to check whether we have more elements to traverse.

EXAMPLE

  1. java.util.Iterator
  2. java.util.Enumeration

CLASS DIAGRAM

ENTITIES

The following entities or component will be involved in Iterator Design Pattern.
  1. Iterator
  • It is an interface defined with basic methods for accessing elements and traversing the elements.
  1. ConcreteIterator
  • Implementation of Iterator interface and keep track of all the traversal details.
  1. Aggregate
  • It is also an interface to create Iterator Object.
  1. ConcreteAggregate
  • Implementation of Aggregate Interface

USECASE

Getting the final approval for the student to receive the certificate.

PROBLEM

Usually, the college will have two different application for Finance Department and Library Department.
Each may have its own implementation. Consider Finance application has the Array implementation and Library application has the List implementation.
Create Library application (LibraryDepartment.java).
import java.util.ArrayList;
import java.util.List;

import jbr.iteratorpattern.common.Student;

public class LibraryDepartment {

 // list of student who are all got book from library.
 private List<Student> issuedTo;
 
 public LibraryDepartment() {
   issuedTo = new ArrayList<Student>();

   addStudent("Anbu", "CSE", 10001, "chennai", 98439843);
   addStudent("Bala", "IT", 20001, "bangalore", 93343433);
   addStudent("Chandru", "MECH", 30001, "chennai", 98456653);
 }

 public void addStudent(String name, String department, int regNo, String address, long phone) {
   Student student = new Student(name, department, regNo, address, phone);
   issuedTo.add(student);
 }

 // who are all got this book from library.
 public List<Student> getStudents() {
   return issuedTo;
 }

}

Create another class (LibraryClearance.java) which will provide the student report for the clearance process.
import jbr.iteratorpattern.common.Student;

public class LibraryClearance {

 public void getReport() {
   LibraryDepartment libraryDepartment = new LibraryDepartment();

   for (Student student : libraryDepartment.getStudents()) {

     System.out.println("Reg No: " + student.getRegNo());
     System.out.println("Name: " + student.getName());
     System.out.println("Department: " + student.getDepartment());
     System.out.println("Address: " + student.getAddress());
     System.out.println("Phone: " + student.getPhone());
     System.out.println("\n");
   }
 }
}

Now create the Finance application which implements the Array Concept.
public class FinanceDeptartment {

 static final int MAX_STUDENTS = 2;
 int noOfStudents = 0;

 // students who are all yet to pay the fees.
 Student[] feesBalance;

 public FinanceDeptartment() {
   feesBalance = new Student[MAX_STUDENTS];

   addStudent("Anbu", "CSE", 10001, "chennai", 98439843);
   addStudent("Bala", "IT", 20001, "bangalore", 93343433);
 }

 public void addStudent(String name, String department, int regNo, String address, long phone) {
   Student student = new Student(name, department, regNo, address, phone);

   if (noOfStudents >= MAX_STUDENTS) {
     System.out.println("Sorry maximum students reached. Pay Attention!!!");
   } else {
     feesBalance[noOfStudents] = student;
     noOfStudents = noOfStudents + 1;
   }
 }

 public Student[] getStudents() {
   return feesBalance;
 }
}

Now create a class for finance clearance.(FinanceClearance.java)
import jbr.iteratorpattern.common.Student;

public class FinanceClearance {

 public void getReport() {
   FinanceDeptartment financeDepartment = new FinanceDeptartment();
   Student[] students = financeDepartment.getStudents();

   for (int i = 0; i < students.length; i++) {
     Student student = students[i];

     System.out.println("Reg No: " + student.getRegNo());
     System.out.println("Name: " + student.getName());
     System.out.println("Department: " + student.getDepartment());
     System.out.println("Address: " + student.getAddress());
     System.out.println("Phone: " + student.getPhone());
     System.out.println("\n");
   }
 }
}

Now create a client to proceed clearance process.
(IteratorPatternProblem .java)
public class IteratorPatternProblem {

 public static void main(String[] args) {

   System.out.println("FINANCE REPORTS");
   System.out.println("=========================");
   FinanceClearance financeClearance = new FinanceClearance();
   financeClearance.getReport();

   System.out.println("\nLIBRARY REPORTS");
   System.out.println("=========================");
   LibraryClearance libraryClearance = new LibraryClearance();
   libraryClearance.getReport();

 }
}

OUTPUT


FINANCE REPORTS
=========================
Reg No: 10001
Name: Anbu
Department: CSE
Address: chennai
Phone: 98439843


Reg No: 20001
Name: Bala
Department: IT
Address: bangalore
Phone: 93343433



LIBRARY REPORTS
=========================
Reg No: 10001
Name: Anbu
Department: CSE
Address: chennai
Phone: 98439843


Reg No: 20001
Name: Bala
Department: IT
Address: bangalore
Phone: 93343433


Reg No: 30001
Name: Chandru
Department: MECH
Address: chennai
Phone: 98456653

The problem with the above implementation are
  1. The client has to write separate logic to access both applications.
  2. Iteration logic is tightly coupled.

SOLUTION

Through Iterator Design Pattern, we can solve this problem.
Here
Iterator -> StudentIterator
ConcreateIterator -> LibraryDepartmentIterator & FinanceDepartmentIterator
Aggregate -> Department
ConcreateAggregate -> LibraryDepartment & FinanceDepartment

Step 1

The clearance process basically checks whether a student has any pending item to be completed (library - books to return & finance - fees balance).
So define an interface for the Student.
public interface StudentIterator {
 boolean hasNext();
 Object next();
}

Also, define an another interface for Department.
public interface Department {
 StudentIterator getStudents();
}

Step 2

Now Create Iterators for Library & Finance Department which needs to implement the Student Iterator.
LibraryDepartmentIterator.java
import java.util.List;

import jbr.iteratorpattern.common.Student;

public class LibraryDepartmentIterator implements StudentIterator {
 List<Student> students;
 int index = 0;

 public LibraryDepartmentIterator(List<Student> students) {
   this.students = students;
 }

 @Override
 public boolean hasNext() {
   if (index < students.size())
     return true;
   else
     return false;
 }

 @Override
 public Object next() {
   Student student = students.get(index);
   index = index + 1;

   return student;
 }
}

FinanceDepartmentIterator.java
import jbr.iteratorpattern.common.Student;

public class FinanceDepartmentIterator implements StudentIterator {

 Student[] students;
 int index = 0;

 public FinanceDepartmentIterator(Student[] students) {
   this.students = students;
 }

 @Override
 public boolean hasNext() {
   if (index >= students.length || students[index] == null)
     return false;
   else
     return true;
 }

 @Override
 public Object next() {
   Student student = students[index];
   index = index + 1;

   return student;
 }
}

Step 3

Existing Library & Finance Department has to be modified as below.
LibraryDepartment.java
import java.util.ArrayList;
import java.util.List;

import jbr.iteratorpattern.common.Student;

public class LibraryDepartment implements Department {

 // list of student who are all got book from library.
 private List<Student> issuedTo;

 public LibraryDepartment() {
   issuedTo = new ArrayList<Student>();

   addStudent("Anbu", "CSE", 10001, "chennai", 98439843);
   addStudent("Bala", "IT", 20001, "bangalore", 93343433);
   addStudent("Chandru", "MECH", 30001, "chennai", 98456653);
 }

 public void addStudent(String name, String department, int regNo, String address, long phone) {
   Student student = new Student(name, department, regNo, address, phone);
   issuedTo.add(student);
 }

 /**
  * who are all got this book from library
  */
 @Override
 public StudentIterator getStudents() {
   return new LibraryDepartmentIterator(issuedTo);
 }
}

FinanceDepartment.java
import jbr.iteratorpattern.common.Student;

public class FinanceDepartment implements Department {

 static final int MAX_STUDENTS = 2;
 int noOfStudents = 0;

 // students who are all yet to pay the fees.
 Student[] feesBalance;

 public FinanceDepartment() {
   feesBalance = new Student[MAX_STUDENTS];

   addStudent("Anbu", "CSE", 10001, "chennai", 98439843);
   addStudent("Bala", "IT", 20001, "bangalore", 93343433);
 }

 public void addStudent(String name, String department, int regNo, String address, long phone) {
   Student student = new Student(name, department, regNo, address, phone);

   if (noOfStudents >= MAX_STUDENTS) {
     System.out.println("Sorry maximum students reached. Pay Attention!!!");
   } else {
     feesBalance[noOfStudents] = student;
     noOfStudents = noOfStudents + 1;
   }
 }

 /**
  * who are all got this book from library
  */
 @Override
 public StudentIterator getStudents() {
   return new FinanceDepartmentIterator(feesBalance);
 }
}

Step 4

Separate the clearance process from the client and make the client lightweight.
Clearance.java
public class Clearance {
 public void collectReport(Department financeDepartment, Department libraryDepartment) {
   System.out.println("\nFINANCE REPORTS");
   System.out.println("=========================");
   getReport(financeDepartment.getStudents());

   System.out.println("\nLIBRARY REPORTS");
   System.out.println("=========================");
   getReport(libraryDepartment.getStudents());
 }

 public void getReport(StudentIterator studentIterator) {
   while (studentIterator.hasNext()) {
     Student student = (Student) studentIterator.next();

     System.out.println("Reg No: " + student.getRegNo());
     System.out.println("Name: " + student.getName());
     System.out.println("Department: " + student.getDepartment());
     System.out.println("Address: " + student.getAddress());
     System.out.println("Phone: " + student.getPhone());
     System.out.println("\n");
   }
 }
}

Step 5

Now look at our simplified client (IteratorPatternSolution.java) .
public class IteratorPatternSolution {

 public static void main(String[] args) {
   Clearance clearance = new Clearance();
   clearance.collectReport(new FinanceDepartment(), new LibraryDepartment());
 }
}

OUTPUT



FINANCE REPORTS
=========================
Reg No: 10001
Name: Anbu
Department: CSE
Address: chennai
Phone: 98439843


Reg No: 20001
Name: Bala
Department: IT
Address: bangalore
Phone: 93343433



LIBRARY REPORTS
=========================
Reg No: 10001
Name: Anbu
Department: CSE
Address: chennai
Phone: 98439843


Reg No: 20001
Name: Bala
Department: IT
Address: bangalore
Phone: 93343433


Reg No: 30001
Name: Chandru
Department: MECH
Address: chennai
Phone: 98456653

WHEN TO USE?

The iterator design pattern can be used when
  1. Want to provide the access to the elements of an aggregate object without knowing its internal representation.
  2. Want to traverse an aggregate object which consists different data structures.
Hope this example clarifies the Iterator Design Pattern. Please share your comments and suggestions below.

Happy Knowledge Sharing!!!