Chapter 20 Behavioral Pattern—Iterator Pattern


Behavioral patterns are used to describe the complex flow control of programs at runtime, that is, to describe how multiple classes or objects cooperate with each other to complete tasks that a single object cannot complete alone. It involves the allocation of responsibilities between algorithms and objects. Behavioral patterns are divided into class behavior patterns and object behavior patterns:

  • Class behavior pattern: using inheritance mechanism to dispatch behavior between classes

  • Object behavior pattern: Use composition or aggregation to distribute behavior among objects

Since the combination relationship or aggregation relationship is less coupled than the inheritance relationship and satisfies the "principle of composite reuse", the object behavior pattern has greater flexibility than the class behavior pattern.

Behavioral patterns are divided into:

  • template method pattern
  • strategy pattern
  • command mode
  • chain of responsibility model
  • state mode
  • Observer pattern
  • intermediary pattern
  • iterator pattern
  • visitor mode
  • Memo mode
  • interpreter mode

The above 11 behavioral patterns, except for the template method pattern and the interpreter pattern, which are quasi-behavioral patterns, the others all belong to the object behavioral pattern.

iterator pattern

Iterator pattern : Provides an object to sequentially access a series of data in an aggregate object without exposing the underlying representation of the aggregate object (list, stack, tree).

solved problem

The aggregate object is a container. The container can definitely traverse elements. The main responsibility of the container is to store elements efficiently. There are many traversal methods for complex structures (such as trees). If the traversal method is written into the container, the container will be obscured. The main function of an iterator is to efficiently store elements. The iterator is to extract the traversal method from the container, so that when we traverse the container, we interact with the iterator interface without interacting with the container, which conforms to the dependency inversion principle.

structure

  • Aggregate, abstract aggregate role: defines the interface for storing, adding, deleting aggregate elements, and creating iterator objects.
  • ConcreteAggregate, concrete aggregation role: implements an abstract aggregation class and returns an instance of a concrete iterator.
  • Iterator, abstract iterator role: defines an interface for accessing and traversing aggregate elements, usually including hasNext(), next() and other methods.
  • Concretelterator, concrete iterator role: implements the methods defined in the abstract iterator interface, completes the traversal of the aggregate object, and records the current position of the traversal.

example

image-20230525114505262)

Entity class

@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    
    
    private String name;
    private String number;
}

abstract iterator element

public interface StudentIterator {
    //判断是否还有元素
    boolean hasNext();
    //获取下一个元素
    Student next();
}

concrete iterator

public class StudentIteratorImpl implements  StudentIterator{
    
    
    private final List<Student> list;
    private int position=0;// 记录遍历时的位置

    public StudentIteratorImpl(List<Student> list) {
    
    
        this.list = list;
    }

    @Override
    public boolean hasNext() {
    
    
        return position<list.size();
    }

    @Override
    public Student next() {
    
    
        //从集合获取指定位置的元素
        return list.get(position++);
    }
}

abstract aggregate role

public interface StudentAggregate {
    
    
    //添加学生功能
    void addStudent(Student student);
    // 删除学生功能
    void removeStudent(Student stu);
    // 获取迭代器对象功能
    StudentIterator getStudentIterator();
}

specific aggregate role

public class StudentAggregateImpl implements  StudentAggregate{
    
    
    private List<Student> list = new ArrayList<>();
    @Override
    public void addStudent(Student student) {
    
    
        list.add(student);
    }

    @Override
    public void removeStudent(Student student) {
    
    
        list.remove(student);
    }

    @Override
    //获取迭代器对象
    public StudentIterator getStudentIterator() {
    
    
        return new StudentIteratorImpl(list);
    }
}

test

public class Client {
    
    
    public static void main(String[] args) {
    
    
        //创建聚合对象
        StudentAggregateImpl aggregate = new StudentAggregateImpl();
        //添加元素
        aggregate.addStudent(new Student("张三", "001"));
        aggregate.addStudent(new Student("李四", "002"));
        aggregate.addStudent(new Student("王五", "003"));
        aggregate.addStudent(new Student("赵六", "004"));
        //遍历聚合对象
        //1获取迭代器对象
        StudentIterator iterator = aggregate.getStudentIterator();
        //2遍历
        while (iterator.hasNext()){
    
    
            //3获取元素
            Student student = iterator.next();
            System.out.println(student);
        }
    }
Student(name=张三, number=001)
Student(name=李四, number=002)
Student(name=王五, number=003)
Student(name=赵六, number=004)

Problems

advantage:

  • Supports traversing an aggregate object in different ways, and multiple traversal methods can be defined on the same aggregate object. In the iterator pattern, you only need to replace the original iterator with a different iterator to change the traversal algorithm. We can also define a subclass of the iterator ourselves to support new traversal methods.

  • Iterators simplify aggregate classes. Due to the introduction of iterators, there is no need to provide data traversal and other methods in the original aggregate object, which can simplify the design of aggregate classes.

  • In the iterator pattern, due to the introduction of the abstraction layer, it is very convenient to add new aggregate classes and iterator classes without modifying the original code, which meets the requirements of the "opening and closing principle".

shortcoming:

  • The number of classes is increased, which increases the complexity of the system to a certain extent.

Applicable scene

  • When you need to provide multiple traversal methods for aggregate objects.
  • When it is necessary to provide a unified interface for traversing different aggregate structures.
  • When accessing the contents of an aggregate object without exposing its representation of internal details.

JDK source code-Iterator

The iterator pattern is widely used in many collection classes in Java. Let's take a look at how the iterator pattern is used in Java source code.

List<String> list = new ArrayList<>();
Iterator<String> iterator = list.iterator(); // list.iterator()方法返回的肯定是Iterator接口的子实现类对象
while (iterator.hasNext()) {
    
    
    System.out.println(iterator.next());
}

Iterators are used in single-column collections in Java. Take ArrayList as an example:

  • List: abstract aggregate class
  • ArrayList: specific aggregate class
  • Iterator: abstract iterator
  • list.iterator(): returns Iteratora specific iterator object that implements the interface
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
    
    
    
    public Iterator<E> iterator() {
    
    
        return new Itr();
    }
    
    private class Itr implements Iterator<E> {
    
    
        int cursor;       // 下一个要返回元素的索引
        int lastRet = -1; // 上一个返回元素的索引
        int expectedModCount = modCount;

        Itr() {
    
    }
		
        //判断是否还有元素
        public boolean hasNext() {
    
    
            return cursor != size;
        }

        //获取下一个元素
        public E next() {
    
    
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }
        ...
}

This part of the code is relatively simple. It roughly returns an instantiated Iterator object in the iterator method. Itr is an inner class that implements the Iterator interface and overrides its abstract methods.

When we develop in Java, if we want to use the iterator mode, we only need to let our self-defined container class implement java.util.Iterable and implement the iterator() method in it to return an implementation class of java.util.Iterator. That's it.

Guess you like

Origin blog.csdn.net/qq_50985215/article/details/130970513