Thursday, December 1, 2011

Iterator Pattern


Intent
Iterator Pattern provides a way to access the elements of an aggregate object sequentially without exposing its underlying representation       .

It also places the task of traversal on the iterator object, not on the aggregate, which simplifies the aggregate interface and implementation and places the responsibility where it should be.
Typically an Iterator has three tasks that might or might not be implemented in separate methods:
  • Testing whether elements are available
  • Advancing to the next n.th position
  • Accessing the value at the current position
Bidirectional iterators might have additional methods for checking and advancing the reverse direction.
We use iterators quite frequently in everyday life. For example, remote control of TV. We just pick up the TV remote control and start pressing Up and Down or Forward and Back keys to iterate through the channels.
Motivation
Need to traverse different data structures so that algorithms can be defined that are capable of interfacing with each transparently.
Discussion
The idea of the iterator pattern is to take the responsibility of accessing and passing through the objects of the collection and put it in the iterator object. The iterator object will maintain the state of the iteration, keeping track of the current item and having a way of identifying what elements are next to be iterated. 
Moreover, you might want to traverse the list in different ways, depending on what you need to accomplish. But you probably don’t want to bloat the List interface with operations for different traversals, even if you could anticipate the ones you’ll require. You might also need to have more than one traversal pending on the same list. The Iterator pattern lets you do all this.

As an example, if you wanted to support four data structures (array, binary tree, linked list, and hash table) and three algorithms (sort, find, and merge), a traditional approach would require four times three permutations to develop and maintain. Whereas, a generic programming approach would only require four plus three configuration items.
Problem Statement:
We have a have big Car Dealer Showroom in United States which only deals in selling OLD cars. They now decide to grow their business. They have started building relationships with various Car Dealers involved in same business in different cities.
To start off with, they tie up with Sports Car House who only deals in Sports Cars and Heritage Car House who only deals in Heritage Cars. They are very satisfied with the Agreement, but they will definitely face a lot of challenges running such a business.
Iterator Pattern at First Glance:
Shown below is the United States Car House; they want to display the details of all the cars available in all the branches of car with which they have a tie ups.
UnitedStatesCarHouse

Shown below is the Sports Car House and Heritage Car House which are very different from each           other.
SportsCarHouse
HeritageCarHouse

Let us try out Some Coding
Have a look at the below class which are very easy to understand.
public class Car {
      String color;
      String model;
      double price;

      public Car(String color, String model, double price) {
            this.color = color;
            this.model = model;
            this.price = price;
      }

      // We can have the getter and Setters here
      @Override
      public String toString() {
            return color+","+model+","+price;
      }
}
public class SportCarHouse {
      List sportsCars;

      public SportCarHouse() {
            sportsCars = new ArrayList();
            addItem("Black","Audi",3000000);
            addItem("Red","Aston Martin",299999);
            addItem("Yellow","BMW",34966666);
            addItem("Red","Bugatti",35555559);
      }
      public void addItem(String name, String description,double price)
      {
            Car menuItem = new Car(name, description,price);
            sportsCars.add(menuItem);
      }
      public List getCars() {
            return sportsCars;
      }
}

public class HeritageCarHouse{
     
      static final int MAX_ITEMS = 6;
      int numberOfCars = 0;
      Car[] heritageCars;
 
      public HeritageCarHouse() {
      heritageCars = new Car[MAX_ITEMS];
      addItem("Black","Land Rover",3000000);
      addItem("Red","Aston Martin",299999);
      addItem("Yellow","Bentley Heritage",34966666);
      addItem("Green","Austin Mini",35555559);
      }
 
      public void addItem(String name, String model, double price)
      {
      Car menuItem = new Car(name,model,price);
      if (numberOfCars >= MAX_ITEMS) {
      System.err.println("Sorry, Car Hoouse is full!  Can't add more Cars");
      }else {
            heritageCars[numberOfCars] = menuItem;
            numberOfCars = numberOfCars + 1;
      }
      }

      public Car[] getCars() {
            return heritageCars;
      }
 
}

Looking at the above two classes we can easily make out that printing the list all the cars from SportCarHouse and HeritageCarHouse we have to call their getCars() methods which returns a List and an Array Respectively .
 We would need two different logics to print the cars from both the Car Houses dealers. We are also not sure that tomorrow we are not going to add a new  car dealer which returns a SortedTree or a Map of cars. Oh oh oh!!!!! This is getting clumsy.

// To print the array of Heritage Cars

for (int i = 0; i < heritageCars.length; i++) {
      Car car = heritageCars[i];
      System.out.println(car);
}

// To print the list of Sports Cars

for (Iterator iterator = sportsCars.iterator(); iterator.hasNext();) {
      Car car = iterator.next();
      System.out.println(car);
}
NOW WHAT.
Both the Car dealers are putting us in difficulty .They do not want to change their implementations as this would mean them writing a lot of code in their classes. But if one of them doesn’t give up, then we are going to implement the Car Dash Board code which would be very difficult to maintain and extend.
It would be great if we can find out a way to implement a common interface which will perform the looping on the list of cars irrespective of the Data Structure. This sounds good but how to do it.


public interface CarIterator {
     
      public Iterator createIterator();
     
}

public class SportCarHouse implements CarIterator {
     
      /**
       * We can remove this method as we don't require this.
       * This also exposes our car List to outside world
       */
      public List getCars() {
            return sportsCars;
      }
      public Iterator createIterator() {
            return sportsCars.iterator();
      }
}

public class HeritageCarHouse implements CarIterator {
      /**
       * We can remove this method as we don't require this.
       * This also exposes our car List to outside world
       */

      public Car[] getCars() {
            return heritageCars;
      }
 
      public Iterator createIterator() {
            return new HeritageCarArrayIterator(heritageCars);
      }
}
Both the SportsCarHouse and HeritageCarHouse will be implementing CarIteraror interface and override the method createIterartor() which will return a concrete iterator to loop the list/array of the cars.
Now we have only one thing left to be done. Write Concrete Iterartors for both the Car Houses. At last they agreed to make some changes to their code.
Here we can make use of the fact that ArrayList themselves have return an iterator. So we only have to worry for HeritageCarArrayIterator which look something like this.
public class HeritageCarArrayIterator implements Iterator {
      Car[] carList;
      int position = 0;

      public HeritageCarArrayIterator(Car[] carList) {
            this.carList = carList;
      }

      public Car next() {
            Car car = carList[position];
            position = position + 1;
            return car;
      }

      public boolean hasNext() {
            if (position >= carList.length || carList[position] == null) {
                  return false;
            } else {
                  return true;
            }
      }
 
      public void remove() {
            // We can implement this if required
      }
}
Our dash board code will now look very simple and it’s very maintainable and generic.
public class UnitedStateCarHouseDashBoard {
     
private static List carIterator = new ArrayList();
     
      public static void addCarListToBePrinted(CarIterator iterator){
            carIterator.add(iterator);
      }

      public static void printMenu() {
      Iterator iterator = carIterator.iterator();
      while (iterator.hasNext()) {
            CarIterator carListIterator = (CarIterator) iterator.next();
            printMenu(carListIterator.createIterator());
            }
      }
      public static void printMenu(Iterator iterator) {
      while (iterator.hasNext()) {
            Car car = iterator.next();
            System.out.println(car);
                 
            }
      }
}

It’s time to test out code have a first look at our Dash Board.
public class UnitedStatesCarHouse {
     
      public static void main(String[] args) {
            SportCarHouse sch = new SportCarHouse();
            HeritageCarHouse hch = new HeritageCarHouse();
            UnitedStateCarHouseDashBoard.addCarListToBePrinted(sch);
            UnitedStateCarHouseDashBoard.addCarListToBePrinted(hch);
            UnitedStateCarHouseDashBoard.printMenu();
            UnitedStateCarHouseDashBoard.printMenu(sch.createIterator());
           
      }
}

Out Put is:

Black,Audi,3000000.0
Red,Aston Martin,299999.0
Yellow,BMW,3.4966666E7
Red,Bugatti,3.5555559E7

Black,Land Rover,3000000.0
Red,Aston Martin,299999.0
Yellow,Bentley Heritage,3.4966666E7
Green,Austin Mini,3.5555559E7

We are able to achieve what we wanted. Now we are only left with one thing to grow our business, establish tie up with more and more car dealers.
 Class Diagram
Class Diagram
Check list
  1. Add a createIterator() method to the “collection” class, and grant the “iterator” class privileged access.
  2.  Design an “iterator” class that can encapsulate traversal of the “collection” class.
  3.  Clients ask the collection object to create an iterator object.
  4. Clients use the first(), remove(), next(), and hasNext() protocol to access the elements of the collection class.



2 comments:

  1. With iterators, the inability to get the count/size sometimes makes it awkward. Usually, one'd have a separate method to query the size but in doing so we would have split the two operations.
    So, it might not play well with UI components that do pagination.
    How about using a lazy collection instead? At least the Hibernate folks took that route.
    I do concede that a lazy collection would still not play with pagination though.

    ReplyDelete
  2. Choosing of design patterns to be used for a given problem depends on a lot of factors..Different approaches may be followed in different scernios

    ReplyDelete