This is the 19th day of my participation in the August Wenwen Challenge.More challenges in August

In the book JAVA and Pattern by Dr. Yan Hong, the Iterator pattern is described as follows: The Iterator pattern is also called Cursor pattern, which is the behavior pattern of objects. The iterative subpattern can access elements in an aggregation sequentially without exposing the internal representation of the aggregation.

The iterator pattern is defined by providing a way to access elements in an aggregate object sequentially without exposing the internal representation in that aggregate object. The iterator pattern is derived from “access” to aggregate objects (arrays, collections, linked lists). The aggregate object is traversed by defining different traversal strategies.

Application scenarios

  1. The iterator pattern can be used when you want to provide access to the content of an aggregate object without exposing its internal representation
  2. You can access an aggregate object if you want more than one traversal.
  3. If you want to provide a uniform interface for traversing different aggregate objects, you can use the iterator pattern (polymorphic iteration).

UML diagrams

Roles involved in the iterator pattern:

  • Iterator: This abstract role defines the interface methods for iterating through elements.
  • Concreteiterators: Implement concrete iterators, inherit or implement iterators.
  • Aggregate: Defines an abstraction of Aggregate objects and defines concrete Iterator interfaces to return Iterator types
  • ConcreteAggregate: Implements an interface for creating iterators that return an appropriate concrete Iterator.

Case presentation

First create the Iterator Iterator interface

/** * defines the iterator role *@author Iflytek_dsw
 *
 */
interface Iterator {
	/** * gets the first element *@return* /
	String first(a);
	/** * gets the last element *@return* /
	String last(a);
	/** * determines if there is a next element *@return* /
	boolean hasNext(a);
	/** * the next element *@return* /
	String next(a);
}
Copy the code

The Iterator interface defines the traversal operations required to aggregate objects.

Define abstract aggregation objects

/** * Aggregate roles to define the interface * for aggregate roles@author Iflytek_dsw
 *
 */
abstract class Aggregate {

	abstract Iterator iterator(a);
}
Copy the code

Define concrete aggregation objects

class ConcreteAggregate extends Aggregate{
	
	private List<String> names;

	public ConcreteAggregate(List<String> names) {
		super(a);this.names = names;
	}

	@Override
	Iterator iterator(a) {
		return new AggregateIterator(this);
	}
	
	public String first(a){
		return names == null ? null : names.get(0);
	}
	
	public String last(a){
		return names == null ? null : names.get(names.size() -1);
	}
	
	public String next(int index){
		return names == null ? null : names.get(index);
	}
	
	/** * The number of elements in the aggregation *@return* /
	public int size(a){
		returnnames.size(); }}Copy the code

In the above example, we can see that in the concrete aggregation, we define methods with Iterator objects that encapsulate concrete operations. To call from a concrete instance of Iterator, we also encapsulate a method that generates Iterator.

Concrete iterator

class AggregateIterator implements Iterator{
	private ConcreteAggregate concreteAggregate;
	private int index;
	public AggregateIterator(ConcreteAggregate concreteAggregate) {
		super(a);this.concreteAggregate = concreteAggregate;
		this.index = 0;
	}

	@Override
	public String first(a) {
		return concreteAggregate.first();
	}

	@Override
	public String last(a) {
		return concreteAggregate.last();
	}

	@Override
	public String next(a) {
		return concreteAggregate.next(index -1);
	}

	@Override
	public boolean hasNext(a) {
		if(index < concreteAggregate.size()){
			index++;
			return true;
		}
	    return false; }}Copy the code

Define an instance containing an aggregation object in a concrete iterator, that is, access to the corresponding operation on the aggregation object.

The client

public class Client {

	/ * * *@param args
	 */
	public static void main(String[] args) {
		List<String> names = new ArrayList<>(Arrays.asList(new String[]{"James"."Lucy"."Jack"}));
		Aggregate aggregate = new ConcreteAggregate(names);
		Iterator iterator = aggregate.iterator();
		System.out.println("The first element is:" + iterator.first());
		System.out.println("The last element is:" + iterator.last());
		
		while(iterator.hasNext()){
			System.out.println("Traversal elements :"+ iterator.next()); }}}Copy the code

In the above client, we first create an aggregation class instance and then call the iterator method to get an iterator role. Once we get the iterator role, we can perform related traversal operations.

The execution result

The first element is James and the last element is Jack traversal element :James traversal element :Lucy traversal element :JackCopy the code

From the above example, we can get the following columns:

  1. The essence of an iterator: Control access to elements in an aggregate object. Iterators provide “transparent access to elements of an aggregate without exposing the internal implementation of the aggregate.” Note that iterators can have both “transparent” access and “controlled access” to aggregate objects.
  2. The key idea of iterators is to separate traversal access to the aggregate object from the aggregate object into a separate iterator, so that the aggregate object becomes simpler, and the iterator and aggregate object can change and evolve independently, increasing the flexibility of the system.
  3. Motivation for iterators: The internal structure of collection objects often varies during software construction. But for these collection objects, we want to make the elements contained within them transparently accessible to external client code without exposing their internal structure; At the same time, this “transparent traversal” also makes it possible for “the same algorithm to operate on multiple sets of objects”. Abstracting the traversal mechanism as an “iterator object” provides an elegant way to “deal with changing collection objects.

In the above example, why should we use the iterator pattern now that the aggregation is already provided internally? Indeed, the client can according to the traverse together provide interfaces to operating, but the iterator pattern will be the iteration abstraction well, has better expansibility, because collection objects changing internal structure, use the iterator pattern can be to separate the responsibility of the client and gather, make two are independent evolution, the iterator pattern as an intermediary, can absorb the changes, Avoid client modification.

Internal iteration vs. external iteration

An external iterator refers to a step in which the client controls the iteration of the next element. The client explicitly calls iteration methods such as next() of the iterator to move forward in the loop.

An inner iterator is the iterator itself that controls the iteration of the next element. Therefore, if you want to do work during an iteration, the client needs to pass the operation to the iterator, which performs the operation on each element as it iterates, similar to JAVA’s callback mechanism.

External iterators are generally more flexible than internal iterators, so most of our common implementations are active iterators.

If a clustered interface does not provide methods to modify the clustered elements, such an interface is called a narrow interface.

Aggregation objects provide a wide interface for iterators and a narrow interface for other objects to access. That is, the aggregation object is properly exposed to the iterator so that the iterator knows enough about the aggregation object to iterate over it. However, aggregate objects should avoid providing these methods to other objects, which should do so through iterators and not directly manipulate aggregate objects.

In Java, the way to implement this double-bound interface is through inner classes, where the iterator object is set to be the inner class of the aggregation class, so that the iterator object has good access to the aggregation class, while limiting the external operations. This kind of scheme that guarantees the encapsulation of aggregation object and the implementation of iteration sub-function is called black box implementation scheme. An iterator defined in this form is called an inner iterator.

See the following example:

public class NameRepository implements Container {
   public String names[] = {"Robert" , "John" ,"Julie" , "Lora"};

   @Override
   public Iterator getIterator(a) {
      return new NameIterator();
   }

   private class NameIterator implements Iterator {

      int index;

      @Override
      public boolean hasNext(a) {
         if(index < names.length){
            return true;
         }
         return false;
      }

      @Override
      public Object next(a) {
         if(this.hasNext()){
            return names[index++];
         }
         return null; }}}Copy the code

Supplementary knowledge — static and dynamic iterators

Static iterators Static iterators are created by the clustered object and hold a snapshot of the clustered object. The contents of this snapshot remain unchanged after generation. The client can continue to modify the contents of the original aggregation, but iterating over the child objects does not reflect new changes to the aggregation.

The benefits of static iterators are their security and simplicity; in other words, static iterators are easy to implement and less prone to errors. However, since static iterators make a copy of the original aggregation, its disadvantage is the consumption of time and memory resources.

Dynamic iterators Dynamic iterators are the exact opposite of static iterators. After they are generated, iterators retain a reference to the aggregation element, so any changes to the original aggregation are reflected in the iterator child.

Full dynamic iterators are not easy to implement, but simplified dynamic iterators are not. The iterators that most JAVA designers encounter are these simplified dynamic iterators. To illustrate what a simplified dynamic iterator is, we first need to introduce a new concept: Fail Fast.

Fail Fast If, after an algorithm has started, its operating environment changes so that the algorithm cannot make the necessary adjustments, the algorithm should immediately signal failure. That’s what Fail Fast means.

If the elements of an aggregation object change during the iteration of a dynamic iterator, the iteration process is affected and becomes self-destructive. At this point, the iterator should immediately throw an exception. Such iterators are those that implement Fail Fast functionality.

Fail Fast uses the JAVA language in JAVA aggregation to support the iterative subpattern in the form of the java.util.iterator interface. The Collection interface requires an Iterator () method that returns an object of type Iterator when called. AbstractList Itr, an inner member of the Collection interface subtype, is the class that implements the Iterator interface.

The advantages and disadvantages

advantages

  1. Better encapsulation, aggregation objects can be accessed without exposing the internal implementation;
  2. Each aggregation object can have one or more iteration children, and the iteration state of each iteration child can be independent of each other. Thus, an aggregation object can have several iterations in progress at the same time.
  3. The separation of the aggregated content and iterators improves extensibility, meaning that different iterators can be used to traverse the aggregated content.

disadvantages

  1. Too many types of iterators will increase the number of classes and improve the complexity of the system.
  2. Exceptions are prone to occur in dynamic iterators.

The resources

  • JAVA & Patterns Iterative subpatterns: www.cnblogs.com/java-my-lif…