Iterator pattern - traverse the elements in the aggregate object

1 Introduction

1.1. Overview

In software development, it is often necessary to use aggregate objects to store a series of data. Aggregation objects have two responsibilities: one is to store data; the other is to traverse data. From the perspective of dependency, the former is the basic responsibility of aggregate objects; while the latter is both changeable and separable. Therefore, the behavior of traversing data can be separated from the aggregation object and encapsulated in an object called an "iterator". The behavior of traversing the internal data of the aggregate object is provided by the iterator, which will simplify the design of the aggregate object and better meet the requirements of the single responsibility principle.

1.2. Definition

Iterator Pattern: Provides a way to access the aggregated object without exposing the internal representation of the object, its alias for the cursor (Cursor). The iterator pattern is an object behavioral pattern.

2. Analysis

2.1, UML class diagram

The iterator pattern structure contains two hierarchies of aggregation and iterators. Considering the flexibility and scalability of the system, the factory method pattern is applied in the iterator pattern, and its pattern structure is shown in the figure below.
insert image description here
It can be seen that the following four roles are included in the iterator pattern structure diagram:

  1. Iterator (abstract iterator): It defines an interface for accessing and traversing elements, and declares methods for traversing data elements. For example, the first() method for getting the first element, the next() method for accessing the next element, the hasNext() method for judging whether there is a next element, the currentItem() method for getting the current element( ) method, etc. These methods will be implemented in concrete iterators.
  2. ConcreteIterator (concrete iterator): It implements the abstract iterator interface, completes the traversal of the aggregate object, and records the current position in the aggregate object through the cursor in the concrete iterator. When implemented, the cursor is usually a non-negative integer representing the position.
  3. Aggregate (abstract aggregation class): It is used to store and manage element objects, declare a createIterator() method to create an iterator object, and act as an abstract iterator factory.
  4. ConcreteAggregate (concrete aggregate class): It implements the createIterator() method declared in the abstract aggregate class, which returns a concrete iterator ConcreteIterator instance corresponding to the concrete aggregate class.

2.2. Code example

The factory method pattern is applied in the iterator pattern, the abstract iterator corresponds to the abstract product role, the concrete iterator corresponds to the concrete product role, the abstract aggregate class corresponds to the abstract factory role, and the concrete aggregate class corresponds to the concrete factory role.

The method used to traverse the elements stored in the aggregate object is declared in the abstract iterator, and the typical code is as follows:

interface Iterator{
    
    
   public void first(); // 将游标指向第一个元素
   public void next(); // 将游标指向下一个元素
   public boolean hasNext(); // 判断是否存在下一个元素
   public Object currentItem(); // 获取游标指向的当前元素
}

The traversal data method declared in the abstract iterator will be implemented in the concrete iterator, the code is as follows:

class ConcreteIterator implements Iterator{
    
    
  // 维持一个对具体聚合对象的引用,以便于访问存储在聚合对象中的数据
  private int cursor; // 定义一个游标,用于记录当前访问位置
  public ConcreteIterator(ConcreteAggregate object){
    
    
     this.object=object;
  }

   public void first(){
    
    
   
   }
   public void next(){
    
    

   }
   public boolean hasNext(){
    
    
    
   }
   public Object currentItem(){
    
    

   }
}

It should be noted that the design of the abstract iterator interface is very important. On the one hand, it needs to fully meet the requirements of various traversal operations, and try to provide declarations for various traversal methods; on the other hand, it cannot contain too many methods. Too many methods in the interface will bring trouble to the implementation of subclasses. Therefore, you can consider using abstract classes to design abstract iterators, and provide an empty default implementation for each method in the abstract class. If you need to add a new traversal operation to the aggregate object in the concrete iterator, you must modify the source code of the abstract iterator and the concrete iterator, which will violate the principle of opening and closing. Therefore, you must consider it comprehensively during design and avoid modifying the interface later.

Aggregate classes are used to store data and are responsible for creating iterator objects. The simplest abstract aggregation class code is as follows:

interface Aggregate{
    
    
  Iterator createIterator();
}

As a subclass of the abstract aggregate class, the concrete aggregate class is responsible for storing data on the one hand, and on the other hand implements the factory method createIterator() declared in the abstract aggregate class to return a specific iterator object corresponding to the concrete aggregate class . code show as below:

class ConcreteAggregate implements Aggregate{
    
    
   
  public Iterator createIterator(){
    
    
      return new ConcreteIterator(this); 
   }

}

3. Summary of iterator mode

The iterator pattern is a very frequently used design pattern. By introducing an iterator, the data traversal function can be separated from the aggregation object. Aggregation objects are only responsible for storing data, while traversing data is done by iterators. Since the class libraries of many programming languages ​​have already implemented the iterator mode, in actual development, you only need to directly use the iterators defined in Java, C# and other languages. Iterators have become one of the basic tools for manipulating aggregated objects.

3.1. Main advantages

  1. It supports traversing an aggregation object in different ways, and multiple traversal methods can be defined on the same aggregation object. In the iterator mode, you only need to replace the original iterator with a different iterator to change the traversal algorithm, and you can also define the subclass of the iterator to support the new traversal method.
  2. Iterators simplify aggregation classes. Due to the introduction of iterators, there is no need to provide methods such as data traversal in the original aggregation objects, which can simplify the design of aggregation classes.
  3. In the iterator mode, due to the introduction of the abstraction layer, it is very convenient to add new aggregation classes and iterator classes, without modifying the original code, and meeting the requirements of the open-close principle.

3.2. Main disadvantages

  1. Since the iterator mode separates the responsibilities of storing data and traversing data, adding a new aggregation class requires correspondingly adding a new iterator class, and the number of classes increases in pairs, which increases the complexity of the system to a certain extent.
  2. The design of the abstract iterator is relatively difficult, and the future expansion of the system needs to be fully considered. For example, JDK's built-in iterator Iterator cannot realize reverse traversal. If reverse traversal is required, it can only be realized through its subclass ListIterator, etc., and ListIterator iterator cannot be used to operate Set type aggregate objects. When customizing iterators, it is not easy to create a comprehensive abstract iterator.

3.3. Applicable scenarios

  1. Access the contents of an aggregate object without exposing its internal representation. The access of the aggregated object is separated from the storage of internal data, so that the internal implementation details of the aggregated object do not need to be understood when accessing the aggregated object.
  2. It is necessary to provide multiple traversal methods for an aggregate object.
  3. Provide a unified interface for traversing different aggregation structures, and provide different traversal methods for different aggregation structures in the implementation class of the interface, and the client can operate the interface consistently.

Guess you like

Origin blog.csdn.net/YYBDESHIJIE/article/details/132059841