迭代器模式

59 阅读4分钟

Iterator

定义

让你能在不暴露集合底层表现形式(列表、栈和树等) 的情况下遍历集合中所有的元素。

应用场景

1.集合为复杂的数据结构,且希望对客户端隐藏其复杂性时(出于使用便利性或安全性的考虑)

迭代器封装了与复杂数据结构进行交互的细节,为客户端提供多个访问集合元素的简单方法。这种方式不仅对客户端来说非常方便,而且能避免客户端在直接与集合交互时执行错误或有害的操作,从而起到保护集合的作用。

2.使用该模式可以减少程序中重复的遍历代码

重要迭代算法的代码往往体积非常庞大。当这些代码被放置在程序业务逻辑中时,它会让原始代码的职责模糊不清, 降低其可维护性。 因此,将遍历代码移到特定的迭代器中可使 程序代码更加精炼和简洁。

3.希望代码能够遍历不同的甚至是无法预知的数据结构

该模式为集合和迭代器提供了一些通用接口。如果你在代码中使用了这些接口,那么将其他实现了这些接口的集合和迭 代器传递给它时,它仍将可以正常运行。

实现方式

1.声明迭代器接口。该接口必须提供至少一个方法来获取集合 中的下个元素。 但为了使用方便,你还可以添加一些其他方法, 例如获取前一个元素、 记录当前位置和判断迭代是否已结束。

2.声明集合接口并描述一个获取迭代器的方法。 其返回值必须是迭代器接口。 如果你计划拥有多组不同的迭代器, 则可以声明多个类似的方法。

3.为希望使用迭代器进行遍历的集合实现具体迭代器类。 迭代器对象必须与单个集合实体链接。 链接关系通常通过迭代器的构造函数建立。

4.在你的集合类中实现集合接口。 其主要思想是针对特定集合为客户端代码提供创建迭代器的快捷方式。集合对象必须将自身传递给迭代器的构造函数来创建两者之间的链接。

5.检查客户端代码, 使用迭代器替代所有集合遍历代码。 每当客户端需要遍历集合元素时都会获取一个新的迭代器。

优缺点

优点:

1.单一职责原则。 通过将体积庞大的遍历算法代码抽取为独立的类, 你可对客户端代码和集合进行整理。

2.开闭原则。 你可实现新型的集合和迭代器并将其传递给现有代码,无需修改现有代码。

3.你可以并行遍历同一集合, 因为每个迭代器对象都包含其自身的遍历状态。

4.相似的,你可以暂停遍历并在需要时继续。

缺点:

1.如果你的程序只与简单的集合进行交互, 应用该模式可能会 矫枉过正。

2.对于某些特殊集合, 使用迭代器可能比直接遍历的效率低。

结构

UML图

classDiagram
Client --> Iterator
Client -->Aggregate
ConcreteAggregate --|> Aggregate
ConcreteIterators--|>Iterator
ConcreteIterators --> ConcreteAggregate
ConcreteAggregate ..> ConcreteIterators

参与者

1.迭代器(Iterator)接口声明了遍历集合所需的操作:获取下一个元素、 获取当前位置和重新开始迭代等。

2.具体迭代器(Concrete Iterators)实现遍历集合的一种特定 算法。 迭代器对象必须跟踪自身遍历的进度。 这使得多个迭 代器可以相互独立地遍历同一集合。

3.聚合(Aggregate)接口声明一个或多个方法来获取与集合兼 容的迭代器。 请注意, 返回方法的类型必须被声明为迭代器 接口, 因此具体集合可以返回各种不同种类的迭代器。

4.具体聚合(ConcreteAggregate)会在客户端请求迭代器时 返回一个特定的具体迭代器类实体。 你可能会琢磨, 剩下的 集合代码在什么地方呢? 不用担心, 它也会在同一个类中。 只是这些细节对于实际模式来说并不重要, 所以我们将其省 略了而已。

5.客户端(Client)通过集合和迭代器的接口与两者进行交互。 这样一来客户端无需与具体类进行耦合, 允许同一客户端代 码使用各种不同的集合和迭代器。

客户端通常不会自行创建迭代器, 而是会从集合中获取。 但 在特定情况下, 客户端可以直接创建一个迭代器(例如当客 户端需要自定义特殊迭代器时)。

通用写法

public interface Iterator<E> {
   E next();
​
   boolean hasNext();
}
​
public interface Aggregate<E> {
   boolean add(E element);
​
   boolean remove(E element);
​
   Iterator<E> iterator();
}
​
public class ConcreteIterator<E> implements Iterator<E> {
   private List<E> list;
   private int cursor;
​
   public ConcreteIterator(List<E> list){
       this.list = list;
  }
​
   public E next(){
       return  this.list.get(this.cursor++);
  }
​
   public boolean hasNext(){
       return this.cursor < this.list.size();
  }
​
}
​
public class ConcreteAggregate<E> implements Aggregate<E> {
   public List<E> list = new ArrayList<>();
​
   public boolean add(E element){
       return  this.list.add(element);
  }
​
   public boolean remove(E element){
       return  this.list.remove(element);
  }
​
   public Iterator<E> iterator(){
       return  new ConcreteIterator<E>(this.list);
  }
}
​
public class Client {
   public static void main(String[] args) {
       Aggregate<String> aggregate = new ConcreteAggregate<String>();
​
       aggregate.add("aaa");
       aggregate.add("bbb");
       aggregate.add("ccc");
​
       Iterator<String> iterator = aggregate.iterator();
​
       while(iterator.hasNext()){
           String element = iterator.next();
           System.out.println(element);
      }
  }
}