The iterative sub-pattern, also called the cursor pattern, is the behavior pattern of the object. Iterative sub-patterns can sequentially access an element in a cluster without exposing the aggregated interior representation (internal representation).
Aggregation and Java Aggregation
Multiple objects are collectively called aggregates (Aggregate), and clustered objects are container objects that can contain a set of objects. Aggregation relies on abstraction of the aggregation structure, which is complex and diverse. Arrays are the most basic aggregation, and are also the basis of other Java aggregation object design.
Java aggregation objects are objects that implement a common Java.util.Collection interface and are the direct support of the Java language to the aggregation concept. Starting with version 1.2, the Java language provides many kinds of aggregation, including vectors, ArrayList, HashSet, HashMap, Hashtable, and so on, all of which are examples of Java aggregation.
Structure of the iterative sub-pattern
There are two implementations of the iterative sub-pattern, namely, the white box aggregation and the outer iteration and the black box aggregation in the intrinsic iteration.
white box aggregation and external iterative sub-generation
If a clustered interface provides a method that can be used to modify a clustered element, this interface is called a wide interface .
If a clustered object provides the same interface for all objects, that is, a wide interface, it will of course satisfy the iterative sub-pattern requirements for the iteration sub-object. However, this destroys the encapsulation of the clustered object. This aggregation, which provides a wide interface, is called a white box aggregation . The clustered object provides the same wide interface to the outside world, as shown in:
By aggregating itself to implement iterative logic and providing an appropriate interface externally, the iteration can control the iterative process of aggregating elements from the outside. As a result, the iteration control is just a cursor, which is called the cursor iteration (cursor Iterator). Since the iteration is outside the aggregation structure, such an iteration is called an external iteration (extrinsic Iterator).
Now take a look at the implementation of the white box aggregation and the external iterative sub-generation. A white box gathers to provide an interface to the outside world to access its own elements (called traversal methods or traversing method), so that the external iteration can iterate through the aggregated traversal method.
Because the logic of the iteration is provided by the aggregation object itself, such an external iterative child role tends to keep only the cursor position of the iteration.
A typical system consisting of a white box aggregation and an outer iteration, as shown in this implementation, the specific iteration child role is an external class, and the specific clustered role provides an interface to the outside world to traverse the aggregation elements.
The iterative sub-pattern involves several roles:
Abstract Iteration Child (Iterator) role: This abstract role defines the interfaces required to traverse an element.
Specific iteration child (concreteiterator) Role: This role implements the iterator interface and maintains the cursor position during iteration.
Aggregation (Aggregate) role: This abstract role gives an interface to create an iterative child (Iterator) object.
Specific aggregation (concreteaggregate) role: Implements an interface that creates an iterative child (Iterator) object, and returns an appropriate iteration child instance.
Client role: holds a reference to the aggregation and its iteration sub-objects, calls the iteration interface of the iteration sub-object, and may also accumulate and delete the elements by iterating through the child operations.
Source
Abstract clustered Role class, which specifies the interfaces that all concrete aggregates must implement. Iterative sub-patterns require that a clustered object must have a factory method, the Createiterator () method, to provide an instance of an iterative child object to the outside world.
Public abstract class Aggregate { /** * Factory method, creating an interface for the corresponding iteration sub-object * /public abstract Iterator Createiterator () ;}
The specific clustered role class implements the interface required by the abstract clustered role class, which is the Createiterator () method. In addition, there are methods for getelement () to provide aggregation elements to the outside world, and the method size () provides the aggregation to the outside world.
public class Concreteaggregate extends Aggregate { private object[] Objarray = null; /** * Construction method, the specific contents of the incoming Aggregation object * /Public concreteaggregate (object[] objarray) { this.objarray = Objarray; } @Override public Iterator createiterator () { return to new Concreteiterator (this); } /** * Value method: Provide aggregation elements to the outside world */public Object getelement (int index) { if (Index < objarray.length) { return objarray[index]; } else{ return null; } } /** * Value method: Provides the size of the aggregation to the outside world * /public int size () { return objarray.length;} }
Abstract Iteration Child Role classes
Public interface Iterator { /** * Iteration method: Moves to the first element * /public void firstly (); /** * Iteration method: Move to next element */public void Next (); /** * Iteration method: Is the last element * /Public Boolean isDone (); /** * Iteration method: Returns the current element * /Public Object CurrentItem ();}
Specific Iteration Child role classes
public class Concreteiterator implements Iterator { //holds the specific aggregated object of the iteration private concreteaggregate agg; Internal index, which records the current iteration to the index location private int index = 0; Records the size of the current clustered object private int size = 0; Public Concreteiterator (Concreteaggregate agg) { This.agg = agg; This.size = Agg.size (); index = 0; } /** * Iteration method: Returns the current element */ @Override public Object CurrentItem () { return agg.getelement (index); } /** * Iterative method: Move to the first element * /@Override public Void firstly () { index = 0; } /** * Iteration method: Is the last element */ @Override public boolean isDone () { return (index >= size); } /** * Iteration method: Move to next element */ @Override public void Next () { if (Index < size) { index + +;}}}
Client class
public class Client {public void operation () { object[] Objarray = {"One", "I", "three", "four", "Five", "Six"};< c2/>//Create aggregation object Aggregate agg = new Concreteaggregate (objarray); The value in the Loop output aggregation object Iterator it = Agg.createiterator (); while (!it.isdone ()) { System.out.println (It.currentitem ()); It.next (); } } public static void Main (string[] args) { Client client = new client (); Client.operation (); }}
The example above first creates an instance of a clustered class and then calls the Factory method Createiterator () of the clustered object to get an iterative sub-object. After the instance of the iteration is obtained, the client begins the iterative process and prints out all the aggregation elements.
The meaning of the external iterative sub-generation
A frequently asked question is: Since the white box aggregation has provided the outside world with the traversal method, the client can already iterate on its own, why should the iterative sub-pattern be applied and create an iterative sub-object to iterate?
The client can of course iterate on its own, not necessarily requiring an iterative sub-object. However, iterative sub-objects and iterative patterns abstract the iterative process, separating the client as the iterative consumer from the iteration responsibility of the iteration owner, allowing the two to evolve independently. When the kind of aggregation object changes, or the iterative method changes, the iteration as a mediation layer can absorb the change factor, and avoid modifying the client or aggregation itself.
In addition, if the system needs to iterate over several different clustered objects at the same time, and the traversal methods provided by these clustered objects are different, it makes sense to use iterative sub-patterns and an external iterative sub-object. Different iterative sub-objects with the same iterative interface handle aggregate objects with different traversal interfaces, allowing the system to use a single, iterative interface for all iterations.
Black box aggregation and intrinsic iterative sub-generation
If a clustered interface does not provide a way to modify a clustered element, such an interface is called a narrow interface .
A clustered object provides a wide interface for an iterative sub-object, and a narrow interface for other objects. In other words, the internal structure of a clustered object should be exposed appropriately to the iteration sub-object so that the iterative sub-object can have sufficient knowledge of the clustered object, allowing for iterative operations. However, a clustered object should avoid providing these methods to other objects, because other objects should do this through iterative sub-objects rather than manipulating the clustered objects directly.
In the Java language, the way to implement a dual interface is to design an iterative subclass as an internal member class of a clustered class. Such an iterative child object will be able to access the internal structure of the clustered object as if it were an internal member of the clustered object. A schematic implementation is given below, showing how the structure of this dual interface arises, and the implementation of the iterative sub-pattern after the dual interface structure is used. This scheme, which guarantees the encapsulation of clustered objects and the implementation of iterative sub-functions, is called a black-box implementation scheme .
Since the iteration is a clustered inner class, the iteration child has free access to the clustered elements, so the iteration can implement its own iterative function and control the iterative logic of the aggregation element. Because iterations are defined within a clustered structure, such iterations are called intrinsic iterations (intrinsic Iterator).
To illustrate the details of the black box scheme, a schematic black box implementation is given here. In this implementation, the aggregation class concreteaggregate contains an internal member class Concreteiterator, which is a concrete iterative subclass that implements an abstract iterative sub-interface, while aggregation does not provide a way for the outside world to access its own internal elements.
Source
Abstract clustered Role class, which specifies the interfaces that all concrete aggregates must implement. Iterative sub-patterns require that a clustered object must have a factory method, the Createiterator () method, to provide an instance of an iterative child object to the outside world.
Public abstract class Aggregate { /** * Factory method, creating an interface for the corresponding iteration sub-object * /public abstract Iterator Createiterator () ;}
Abstract Iteration Child Role classes
Public interface Iterator { /** * Iteration method: Moves to the first element * /public void firstly (); /** * Iteration method: Move to next element */public void Next (); /** * Iteration method: Is the last element * /Public Boolean isDone (); /** * Iteration method: Returns the current element * /Public Object CurrentItem ();}
The specific clustered role class implements the interface required by the abstract aggregation role, which is the Createiterator () method. In addition, the aggregation class has an internal member class Concreteiterator, which implements the interface specified by the abstract iteration child role, and the Factory method Createiterator () returns an instance of the internal member class.
public class Concreteaggregate extends Aggregate {private object[] Objarray = null; /** * Construction method, the specific contents of the incoming Aggregation object */Public concreteaggregate (object[] objarray) {this.objarray = Objarray; } @Override Public Iterator createiterator () {return new concreteiterator (); }/** * Internal member class, specific iteration subclass */Private class Concreteiterator implements Iterator {//Internal index, record the current iteration to the index location private int index = 0; Records the size of the current clustered object private int size = 0; /** * constructor */public Concreteiterator () {this.size = Objarray.length; index = 0; }/** * Iteration method: Returns the current element */@Override public Object CurrentItem () {return obja Rray[index]; }/** * Iterative method: Move to first element */@Override public void () {index = 0; }/** * Iteration method: Is the last element */@OvErride public boolean IsDone () {return (index >= size); }/** * Iteration method: Move to next element */@Override public void Next () {if (Index < size ) {index + +; } } }}
Client class
public class Client {public void operation () { object[] Objarray = {"One", "I", "three", "four", "Five", "Six"};< c4/>//Create aggregation object Aggregate agg = new Concreteaggregate (objarray); The value in the Loop output aggregation object Iterator it = Agg.createiterator (); while (!it.isdone ()) { System.out.println (It.currentitem ()); It.next (); } } public static void Main (string[] args) { Client client = new client (); Client.operation (); }}
The example above first creates an instance of a clustered class and then calls the Factory method Createiterator () of the clustered object to get an iterative sub-object. After the instance of the iteration is obtained, the client begins the iterative process and prints out all the aggregation elements.
Active iteration and passive iterative sub-generation
The active iteration and the passive iteration are also referred to as the outer iteration and the inner iteration sub.
The so-called active (external) iteration, which refers to the step of the client to control the iteration of the next element, the client will obviously call the iteration's next () iterative method, which moves forward during the traversal.
The so-called passive (inner) iteration, refers to the steps by which the iteration's own control iterates over the next element. Therefore, if you want to do the work during the iteration, the client will need to pass the operation to the iteration, and the iteration will perform this operation on each element at the iteration, similar to the Java callback mechanism.
In general, external iterators are more flexible than internal iterators, so our common implementations belong to active iterations.
Static iteration and dynamic iterative sub-generation
A static iteration is created by a clustered object and holds a snapshot of the clustered object (snapshot), and the content of the snapshot is no longer changed after it is produced. The client can continue to modify the contents of the original aggregation, but the iteration sub-object does not reflect a new change in the aggregation.
The advantage of static iteration is its security and simplicity, in other words, static iterations are easy to implement and not prone to errors. However, because the static iteration copies the original aggregation, its disadvantage is the consumption of time and memory resources.
The dynamic iterative generation son is the exact opposite of the static iteration, and after the iteration is produced, the iteration remains a reference to the aggregation element, so any modifications to the original aggregate content are reflected on the iteration sub-object.
Complete dynamic iterations are not easy to implement, but simplified dynamic iterations are not difficult to implement. The iterations that most Java designers encounter are such simplified dynamic iterations. To illustrate what is a simplified dynamic iteration, you first need to introduce a new concept:Fail Fast.
Fail Fast
If an algorithm starts, its computing environment changes, so that the algorithm can not make the necessary adjustments, the algorithm should immediately send a fault signal. That's what fail fast means.
If the elements of a clustered object change during the iteration of a dynamic iteration, the iterative process is affected and becomes unable to self-precisely. At this point, the iteration should throw an exception immediately. This iteration is the iterative sub-function that realizes the fail fast feature.
The use of Fail fast in Java aggregation
The Java language supports iterative sub-patterns in an interface Java.util.Iterator, and the collection interface requires the iterator () method, which returns an object of type iterator when called. As a subtype of the collection interface, the inner member class of the Abstractlist class ITR is the class that implements the iterator interface.
The source code for the ITR class is as follows
Private class Itr implements iterator<e> {/** * Index of element to being returned by subsequent CA ll to next. */INT cursor = 0; /** * Index of element returned by most recent call to next or * previous. Reset To-1 If this element was deleted by a call * to remove. */INT lastret =-1; /** * The Modcount value, the iterator believes that the backing * List should has. If this expectation are violated, the iterator * has detected concurrent modification. */int expectedmodcount = Modcount; public Boolean Hasnext () {return cursor! = size (); } public E Next () {checkforcomodification (); try {E next = get (cursor); Lastret = cursor++; return next; } catch (Indexoutofboundsexception e) {checkforcomodification (); throw new NosuchElementexception (); }} public void Remove () {if (Lastret = =-1) throw new IllegalStateException (); Checkforcomodification (); try {AbstractList.this.remove (Lastret); if (Lastret < cursor) cursor--; Lastret =-1; Expectedmodcount = Modcount; } catch (Indexoutofboundsexception e) {throw new concurrentmodificationexception (); }} final void Checkforcomodification () {if (Modcount! = expectedmodcount) throw new Concurrentmodificationexception (); } }
As you can see from the source code of the Itr class, Method Checkforcomodification () checks whether the aggregated content has just been modified directly by the outside world (not by the method provided by the iteration). This method immediately throws a Concurrentmodificationexception () exception if the aggregated content is modified directly by the outside of the iterative sub-object after the iteration begins.
This means that the abstractlist.itr iteration is a fail-fast iteration.
Benefits of iterative sub-patterns
(1) Iterative sub-mode simplifies the aggregation of interfaces. The iteration has a traversal interface so that the clustered interface does not have to traverse the interface.
(2) Each clustered object can have one or more iteration sub-objects, and the iteration state of each iteration can be independent of each other. Therefore, a clustered object can have several iterations in progress at the same time.
(3) Because the traversal algorithm is encapsulated within the iteration sub-role, the iterative algorithm can be independent of the aggregation role change.
Iterative sub-patterns of Java and patterns