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.