迭代器模式定义
迭代器模式(Iterator),提供一种方法顺序访问一个聚合对象中的各种元素,而又不暴露该对象的内部表示。
Java程序员在使用集合(collection)时,并不需要关注其类型是数组、列表、集合(set)还是其他,有些人并不知道这些集合包其实是使用了迭代器模式来实现的。
目的
迭代器模式提供了一种顺序遍历聚合对象元素而不暴露其内部实现的方法。
实现
迭代器模式基于两个抽象类或接口,可以通过成对的具体类来实现。
image.png
- Aggregate(抽象容器):应该由所有类实现的抽象类,并且可以由迭代器遍历。这对应于java.util.Collection接口。
- Iterator(抽象迭代器):抽象迭代器是迭代器抽象类,它定义遍历容器对象的操作以及返回对象的操作。
- ConcreteAggregate(具体容器):具体容器可以实现内部不同的结构,但会暴露处理遍历容器的具体迭代器。
- ConcreteIterator(具体迭代器):这是处理特定具体容器类的具体迭代器。实际上,对于每个具体容器,必须实现一个具体迭代器。
实例
将书(Book)放到书架(BookShelf)中,并将书名按顺序显示。
Aggregate 接口
所要遍历的集合的接口。实现了该接口的类将成为一个可以保存多个元素的集合,类似数组。
Aggregate接口中声明的方法为iterator,作用为生成一个用于遍历的迭代器。
/**
* @author: Jay Mitter
* @date: 2020-08-23 20:54
* @description: 表示集合的接口
*/
public interface Aggregate<E> {
/**
* 返回迭代器
* @return iterator
*/
Iterator<E> iterator();
}
Iterator 接口
作用为遍历集合中元素,相当于循环语句中的循环变量(for(int i =0;i<arr.lenth;i++
),具体实现一个顺序遍历的迭代器。
hasNext()
方法判断是否存在下一个;
next()
方法获取下一个元素。特殊说明下,next()
方法在获取元素的同时,要将计数器向下一个元素的计数加一。获取的是当前元素,并指向下一个元素。
/**
* @author: Jay Mitter
* @date: 2020-08-23 20:54
* @description: 遍历集合的接口
*/
public interface Iterator<E> {
/**
* 是否有下一个元素
* @return true false
*/
boolean hasNext();
/**
* 获取的是当前元素,并将计数器向下一个元素的计数加一,指向下一个元素
* @return E
*/
E next();
}
BookShelf 类
书架类,作为存放书的集合类,实现Aggregate接口。实现了Aggregate接口的iterator方法。
主要点在iterator()
方法,方法返回了遍历书架时要用的BookShelfIterator类作为书架的迭代器。当外部要遍历书架时会调用该方法。
/**
* @author: Jay Mitter
* @date: 2020-08-25 22:27
* @description: 表示书架的类
*/
public class BookShelf implements Aggregate<Book> {
private List<Book> bookList;
public BookShelf() {
this.bookList = new ArrayList<>();
}
@Override
public Iterator<Book> iterator() {
return new BookShelfIterator(this);
}
public Book getBookAt(int index) {
return this.bookList.get(index);
}
public void appendBook(Book book) {
this.bookList.add(book);
}
public int getLength() {
return bookList.size();
}
}
BookShelfIterator 类
作为一个迭代器,要实现Iterator接口。index为迭代器当前所指向的下标。hasNext判断还有没有下一本。通过下标和总数比较判断。next获取当前书,并指向下一个。
/**
* @author: Jay Mitter
* @date: 2020-08-25 22:30
* @description: 遍历书架的类
*/
public class BookShelfIterator implements Iterator<Book> {
private BookShelf bookShelf;
private int index;
public BookShelfIterator(BookShelf bookShelf) {
this.bookShelf = bookShelf;
this.index = 0;
}
@Override
public boolean hasNext() {
if (index < bookShelf.getLength()) {
return true;
} else {
return false;
}
}
@Override
public Book next() {
Book book = bookShelf.getBookAt(index);
index++;
return book;
}
}
测试:
/**
* 行为型模式——迭代器模式
*/
@Test
public void testBehaviorIterator() {
BookShelf bookShelf = new BookShelf();
bookShelf.appendBook(new Book("Around the World in 80 Days"));
bookShelf.appendBook(new Book("Bible"));
bookShelf.appendBook(new Book("Cinderella"));
bookShelf.appendBook(new Book("Daddy-Long-Legs"));
Iterator<Book> it = bookShelf.iterator();
while (it.hasNext()) {
Book book = it.next();
System.out.println(book.getName());
}
}
为什么使用迭代器模式
如果是数组,直接使用for玄幻语句进行遍历处理不就可以了吗?
为什么要在集合之外引入Iterator角色?
一个重要的理由:引入Iterator后可以将遍历与实现分离开来。
while (it.hasNext()) {
Book book = it.next();
System.out.println(book.getName());
}
这里使用了Iterator的hasNext
方法和next
方法,并没有调用BookShelf的方法。也就是说,这里的while循环并不依赖于BookShelf的实现。
例如在BookShelf的开发人员决定放弃用数组来管理书本,而是java.util.Vector
取而代之,会怎么样呢。不管BookShelf如何变化,只要BookShelf的iterator方法能正确的返回Iterator的实例,即使不对上面的while循环做任何修改,代码都可以正常工作。
对于BookShelf的调用者来说真的太方便了,设计模式的作用就是帮助我们编写可复用的类。
所谓可复用,就是指将类实现为组件,当一个组件发生改变时,不需要对其他组件进行修改或是只需要很小的修改即可应对。这也就能理解为什么在示例程序中iterator方法返回值不是BookShelf类而是iterator类型了。这表明,程序就是要使用iterator的方法进行编程而不是bookshelfiterator的方法。
在Java现有版本中的java.util.Iterator<E>
类和java.util.Collection<E>
类,是实现新容器和迭代器很好的例子。当需要具有特定行为的容器时,我们应该考虑扩展java.collection
包中实现的一个类,而不是创建一个新类。
网友评论