Design pattern - (16) iterator pattern

Keywords: Java Design Pattern

1. Definition

Provides a way to access an aggregate object without exposing the internal representation of the object, alias cursor. The iterator pattern is an object behavior pattern.

2. UML class diagram

  • Abstract container role (Aggregate): it is responsible for providing an interface for creating a specific iterator role, generally an interface, and providing an iterator() method, such as the Collection interface, List interface, Set interface, etc. in java.

  • Concrete aggregate: it is the concrete implementation class that implements the abstract container, such as ArrayList for the ordered List of the List interface, LinkedList for the linked List of the List interface, HashSet for the Hash List of the set interface, etc.

  • Abstract Iterator role: responsible for defining interfaces to access and traverse elements.

  • Concrete iterator role: implement the iterator interface and record the current position in the traversal.

3. Instance

a. Background:

Implementation of forward traversal iterator

  • Provides a way to traverse a collection of objects, also known as cursor mode

  • Aggregate object: stores data. Here, I define it as a Container interface in the code, which is used to obtain the iterator object

  • Iterators: traversing data

b. Instance

Step 1 creates an abstract iterator class to plan what operations are performed

public interface MyIterator {
     // The initial value of the iterator, if necessary, is moved to the - 1 position to facilitate the unity of some operations
     void moveToMinusOne();

     boolean moveToFirst(); // Move cursor to first position

     boolean next(); // Move cursor to next position

     boolean hasNext(); // Determines whether the cursor has the next position

     boolean isFirst(); // Determines whether the current cursor is the first position

     boolean isLast(); // Determines whether the current cursor is the last position

     Object getCurrentObject(); // Gets the object pointed to by the current cursor
}

Step 2: create an abstract container

public interface AggregateContainer {
    MyIterator getMyIterator();
}

Step 3: create a concrete container class to implement the abstract aggregation container class. Since the iterator is implemented to use and operate the concrete container, the concrete iterator class exists as an internal class in the concrete container class and implements the abstract iterator interface

public class ConcreteContainer implements AggregateContainer {

     private List<Object> list = new ArrayList<>();

     public List<Object> getList() {
           return list;
     }

     public void setList(List<Object> list) {
           this.list = list;
     }

     void addObject(Object object) {
           list.add(object);
     }

     void removeObject(Object object) {
           list.remove(object);
     }

     @Override
     public MyIterator getMyIterator() {
           return new ConcreteIterator();
     }

     // Use the definition method of internal class to make ConcreteIterator serve ConcreteContainer
     private class ConcreteIterator implements MyIterator {

           private int cursor = -1; // Used to record the position during traversal

           public void moveToMinusOne() {
                cursor = -1;
           }

           public boolean moveToFirst() {
                if (list.size() >= 1) {
                      cursor = 0;
                      return true;
                }
                return false;

           }

           public boolean next() {
                if (hasNext()) {
                      cursor++;
                      return true;
                }
                return false;
           }

           public boolean hasNext() {
                return cursor + 1 < list.size();
           }

           public boolean isFirst() {
                return cursor == 0;
           }

           public boolean isLast() {
                return cursor == list.size() - 1;
           }

           public Object getCurrentObject() {
                if (cursor >= 0 && cursor < list.size())
                      return list.get(cursor);
                else
                      return null;
           }
     }
}

Step 4: client test

public class Client {
     public static void main(String[] args) {
           ConcreteContainer carNameContainer = new ConcreteContainer();
           // Test add
           carNameContainer.addObject("byd");
           carNameContainer.addObject("bmw");
           carNameContainer.addObject("benz");
           carNameContainer.addObject("audi");
           // Test delete
           carNameContainer.removeObject("bmw");
           // Get the iterator and test isFist() and isLast()
           MyIterator myIterator = carNameContainer.getMyIterator();
           System.out.println("Is first ? : " + myIterator.isFirst());
           System.out.println("Move to first : " + myIterator.moveToFirst());
           System.out.println("Is first now ? : " + myIterator.isFirst());
           // Traversal correlation function test
           System.out.println("--Traversal method 1--");
           if (myIterator.moveToFirst()) {
                do {
                      System.out.println(myIterator.getCurrentObject());
                } while (myIterator.next()); // next() determines whether there is a next, i.e. hasNext(), and if so, moves to the next position and returns true
           }
           System.out.println("Is last ? : " + myIterator.isLast());

           System.out.println("--Traversal method 2--");
           if (myIterator.moveToFirst()) {
                System.out.println(myIterator.getCurrentObject());
                while (myIterator.next()) {
                      System.out.println(myIterator.getCurrentObject());
                }
           }
           System.out.println("Is last ? : " + myIterator.isLast());

           System.out.println("--Traversal method 3--");
           myIterator.moveToMinusOne();
           while (myIterator.next()) {
                System.out.println(myIterator.getCurrentObject());
           }
           System.out.println("Is last ? : " + 
myIterator.isLast());
     }
}

4. Advantages

  • The traversal method is simplified, and it is still troublesome for traversing the object set. For arrays or sequence tables, we can still obtain them through cursors, but users need to traverse the objects themselves on the premise that they know the set well. However, for hash tables, it is troublesome for users to traverse. After introducing the iterator method, it is much easier for users to use.

  • It can provide a variety of traversal methods. For example, for an ordered list, we can provide two iterators: positive traversal and reverse traversal. Users only need our implemented iterators to traverse the set conveniently.

  • The encapsulation is good. Users only need to get the iterator to traverse, and don't care about the traversal algorithm.

5. Shortcomings

  • Because the iterator pattern separates the responsibility of storing data and traversing data, adding a new aggregation class needs to add a new iterator class, and the number of classes increases in pairs, which increases the complexity of the system to a certain extent.

  • Abstract iterators are difficult to design, and future extensions of the system need to be fully considered. For example, the JDK built-in Iterator iterator cannot implement reverse traversal. If reverse traversal is required, it can only be implemented through its subclass ListIterator, and ListIterator Iterator cannot be used to operate aggregate objects of type Set. When customizing iterators, it is not easy to create a comprehensive Abstract Iterator.

6. Applicable scenario

  • Access the contents of an aggregate object without exposing its internal representation. The access of aggregate object is separated from the storage of internal data, so that it is unnecessary to understand its internal implementation details when accessing aggregate object.

  • You need to provide multiple traversal methods for an aggregate object.

  • Provide a unified interface for traversing different aggregate structures, provide different traversal modes for different aggregate structures in the implementation class of the interface, and the client can operate the interface consistently.

Posted by kazer on Sat, 06 Nov 2021 23:11:06 -0700