此文为《图解Java设计模式》读书笔记,如有笔误欢迎评论指正~
迭代器模式(Iterator Pattern):提供一种方法来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。
以遍历书架上的书为例子(书架类BookShelf,书Book)首先不使用迭代器模式:
先给出 书架类BookShelf和书类Book
// 实体类 书 -被遍历的元素
public class Book {
private String name;
public Book(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
//不使用迭代器模式的书架类
public class BookShelf {
private List<Book> books;//该书架的书
public BookShelf() {
this.books = new ArrayList<>();
}
public Book getBookAt(int index) {
return books.get(index);
}
public void appendBook(Book book){
this.books.add(book);
}
public int getLength() {
return books.size();
}
}
直接最终效果,看前后对比,以遍历书架上的书为例子(书架类BookShelf,书Book,后面有实现迭代器模式的详细代码)
使用迭代器模式前
//for循环遍历
System.out.println("for循环遍历");
for (int i = 0; i < bookShelf.getLength(); i++) {
Book book = bookShelf.getBookAt(i);
System.out.println(book.getName());
}
使用迭代器后:
//迭代器遍历
System.out.println("迭代器遍历");
Iterator iterator = bookShelf.iterator();
while (iterator.hasNext()) {
Book book = (Book) iterator.next();//Object转Book类型
System.out.println(book.getName());
}
观察结果可发现迭代器模式的特点:将遍历与实现分离。
具体到代码中就是,for循环里面是需要使用到书架类的具体方法的:getLength()和getBookAt(),而使用了迭代器模式后只需使用Iterator迭代器提供的hasNext()和next()。也就是说,使用迭代器模式去遍历一个表示元素集合的类是不需要了解这个集合类的具体的数据存储形式,具体的遍历方法。将遍历和数据存储和具体遍历方法完全隔离。
迭代器接口,就使用JDK原生的Iterator,源码在java.util.Iterator下。
具体的迭代器类:在本例中集书架的迭代器BookShelfIterator类
public class BookShelfIterator implements Iterator {
private BookShelf bookShelf;
private int index;
public BookShelfIterator(BookShelf bookShelf) {
this.bookShelf = bookShelf;
}
@Override
public boolean hasNext() {
return index < bookShelf.getLength();//boolean表达式
}
@Override
public Object next() {
Book book= bookShelf.getBookAt(index);
index++;
return book;
}
@Override
public void remove() {
//空实现remove方法
}
}
集合接口类:Aggregate ,提供一个创建迭代器的抽象方法
/**
* 迭代器模式
* 集合的接口 提供一个创建迭代器的抽象方法
*/
public interface Aggregate {
Iterator iterator();
}
集合接口Aggregate的实现类,创建出具体的迭代器,在本例中集书架类BookShelf
public class BookShelf implements Aggregate{
private List<Book> books;//该书架的书
public BookShelf() {
this.books = new ArrayList<>();
}
public Book getBookAt(int index) {
return books.get(index);
}
public void appendBook(Book book){
this.books.add(book);
}
public int getLength() {
return books.size();
}
/**
* 返回迭代器
* @return
*/
@Override
public Iterator iterator() {
return new BookShelfIterator(this);
}
}
在最开始的 为什么要用迭代器模式? 已经测试过迭代器模式了。这里再详细的贴一下测试代码:
public class Test {
public static void main(String[] args) {
//BookShelf bookShelf = new BookShelf(4);//
BookShelf bookShelf = new BookShelf();
bookShelf.appendBook(new Book("《Java编程思想》"));
bookShelf.appendBook(new Book("《PHP程序设计》"));
bookShelf.appendBook(new Book("《C++编程》"));
bookShelf.appendBook(new Book("《Python程序设计》"));
//for循环遍历
System.out.println("for循环遍历");
for (int i = 0; i < bookShelf.getLength(); i++) {
Book book = bookShelf.getBookAt(i);
System.out.println(book.getName());
}
//迭代器遍历
System.out.println("迭代器遍历");
Iterator iterator = bookShelf.iterator();
while (iterator.hasNext()) {
Book book = (Book) iterator.next();//Object转Book类型
System.out.println(book.getName());
}
}
}
测试结果
for循环遍历
《Java编程思想》
《PHP程序设计》
《C++编程》
《Python程序设计》
迭代器遍历
《Java编程思想》
《PHP程序设计》
《C++编程》
《Python程序设计》
注意在测试代码中的这一行:
Iterator iterator = bookShelf.iterator();
bookShelf.iterator()返回的是Iterator类型,而不是具体的迭代器BookShelfIterator类型。这表明这段代码是使用Iterator接口中的方法进行操作,而不是BookShelfIterator具体类中的方法。
这样的好处就是对与BookShelf的调用者来说,在这里即Test测试类,不管BookShelf类后面会发生什么变化,比如存放Book类的方式从数组变为List集合,比如该类方法的改变等…只要能够返回正确的迭代器,那么BookShelf的调用者(在这里是Test类)可以在不修改代码的情况下继续使用迭代器遍历。
而如果是使用for循环,由于循环中使用到了BookShelf类的两个方法:getLength(),getBookAt()。那么之后如果发生上述提到的变化,则遍历的代码块也要发生变化。
以此引申出设计模式一个重要的点:不要只使用具体类来编程,而优先使用抽象类或接口来编程。
此文为《图解Java设计模式》读书笔记,如有笔误欢迎评论指正~