一、场景
- 以简化版“图书馆书籍管理系统”介绍迭代器模式的好处。
- 假设图书馆中有多个书架,每个书架上存放着不同数量的书籍。我们需要实现一个功能,能够遍历所有书架上的书籍,并打印出每本书的信息。
二、不采用迭代器模式
1、代码
// 书类
public class Book {
private String title;
public Book(String title) {
this.title = title;
}
public String getTitle() {
return title;
}
}
// 书架类
public class BookShelf {
private Book[] books;
private int last = 0;
public BookShelf(int maxSize) {
this.books = new Book[maxSize];
}
public void appendBook(Book book) {
if (last >= books.length) {
throw new IllegalArgumentException("书架已满");
}
this.books[last] = book;
last++;
}
public int getLength() {
return last;
}
public Book getBookAt(int i) {
if (i < last && i >= 0) {
return books[i];
}
throw new IllegalArgumentException("书架索引越界");
}
}
// 图书馆类
@Getter
@AllArgsConstructor
public enum BookShelfEnum {
CS_BOOK_SHELF("计算机书架", 0),
LITERATURE_BOOK_SHELF("文学书架", 1),
NOVEL_BOOK_SHELF("小说书架", 2);
private final String name;
private final int index;
}
public class Library {
private BookShelf[] bookShelves;
public Library() {
this.bookShelves = new BookShelf[3];
}
public void appendBook(BookShelfEnum shelfEnum , Book book) {
if (shelfEnum == null) {
throw new IllegalArgumentException("书架类型不能为null");
}
BookShelf bookShelf = bookShelves[shelfEnum.getIndex()];
if (bookShelf == null) {
bookShelves[shelfEnum.getIndex()] = new BookShelf(5);
bookShelf = bookShelves[shelfEnum.getIndex()];
}
bookShelf.appendBook(book);
}
public int getNumShelves() {
return bookShelves.length;
}
public BookShelf getBookShelfAt(int i) {
if (i < 0 || i >= bookShelves.length) {
throw new IllegalArgumentException("书架索引越界");
}
return bookShelves[i];
}
}
// 客户端
public class Main {
public static void main(String[] args) {
Library library = new Library();
library.appendBook(BookShelfEnum.CS_BOOK_SHELF, new Book("Java编程思想"));
library.appendBook(BookShelfEnum.CS_BOOK_SHELF, new Book("Effective Java"));
library.appendBook(BookShelfEnum.LITERATURE_BOOK_SHELF, new Book("三国演义"));
library.appendBook(BookShelfEnum.LITERATURE_BOOK_SHELF, new Book("红楼梦"));
library.appendBook(BookShelfEnum.NOVEL_BOOK_SHELF, new Book("白夜行"));
library.appendBook(BookShelfEnum.NOVEL_BOOK_SHELF, new Book("简爱"));
// 遍历所有书籍
for (int i = 0; i < library.getNumShelves(); i++) {
BookShelf bookShelf = library.getBookShelfAt(i);
if (bookShelf == null) {
continue;
}
for (int j = 0; j < bookShelf.getLength(); j++) {
Book book = bookShelf.getBookAt(j);
System.out.println(book.getTitle());
}
}
}
}
2、缺点
不采用迭代器模式时,当我们去遍历图书馆的书籍的时候,不得不知道图书馆是如何存储书籍的。
- 首先,我们得知道图书馆有书架组成,我们需要遍历若干个书架。
- 其次,对于每个书架,如果有书的话,我们要遍历每本书。
但我们并不希望客户端知道图书馆是如何存储书籍的。这时候怎么办?需要迭代器模式。
三、采用迭代器模式
- 迭代器模式是一种行为设计模式, 让我们能在不暴露集合底层存储结构 (列表、 栈和树等) 的情况下遍历集合中所有的元素。
1、代码
// 书架类
public class BookShelf implements Iterable<Book> {
private Book[] books;
private int last = 0;
public BookShelf(int maxSize) {
this.books = new Book[maxSize];
}
public void appendBook(Book book) {
this.books[last] = book;
last++;
}
private Book getBookAt(int index) {
return books[index];
}
private int getLength() {
return last;
}
@Override
public Iterator<Book> iterator() {
return new BookIterator(this);
}
private class BookIterator implements Iterator<Book> {
private BookShelf bookShelf;
private int index;
public BookIterator(BookShelf bookShelf) {
this.bookShelf = bookShelf;
this.index = 0;
}
@Override
public boolean hasNext() {
return index < bookShelf.getLength();
}
@Override
public Book next() {
return bookShelf.getBookAt(index++);
}
}
}
// 图书馆类
public class Library implements Iterable<Book> {
private BookShelf[] bookShelves;
public Library() {
this.bookShelves = new BookShelf[3];
}
public void appendBook(BookShelfEnum shelfEnum, Book book) {
if (shelfEnum == null) {
throw new IllegalArgumentException("书架类型不能为null");
}
BookShelf bookShelf = bookShelves[shelfEnum.getIndex()];
if (bookShelf == null) {
bookShelves[shelfEnum.getIndex()] = new BookShelf(5);
bookShelf = bookShelves[shelfEnum.getIndex()];
}
bookShelf.appendBook(book);
}
@Override
public Iterator<Book> iterator() {
return new LibraryIterator(this);
}
private class LibraryIterator implements Iterator<Book> {
private Library library;
private int bookShelfIndex = 0;
private Iterator<Book> bookIterator;
public LibraryIterator(Library library) {
this.library = library;
}
@Override
public boolean hasNext() {
if (bookIterator != null && bookIterator.hasNext()) {
return true;
}
while (bookShelfIndex < library.bookShelves.length) {
BookShelf bookShelf = library.bookShelves[bookShelfIndex++];
if (bookShelf == null) {
continue;
}
bookIterator = bookShelf.iterator();
if (bookIterator.hasNext()) {
return true;
}
}
return false;
}
@Override
public Book next() {
return bookIterator.next();
}
}
}
// 客户端
public class Main {
public static void main(String[] args) {
Library library = new Library();
library.appendBook(BookShelfEnum.CS_BOOK_SHELF, new Book("Java编程思想"));
library.appendBook(BookShelfEnum.CS_BOOK_SHELF, new Book("Effective Java"));
library.appendBook(BookShelfEnum.LITERATURE_BOOK_SHELF, new Book("三国演义"));
library.appendBook(BookShelfEnum.LITERATURE_BOOK_SHELF, new Book("红楼梦"));
library.appendBook(BookShelfEnum.NOVEL_BOOK_SHELF, new Book("白夜行"));
library.appendBook(BookShelfEnum.NOVEL_BOOK_SHELF, new Book("简爱"));
// 遍历所有书籍
Iterator<Book> iterator = library.iterator();
while (iterator.hasNext()) {
Book book = iterator.next();
System.out.println(book.getTitle());
}
}
}
2、优点
- 采用迭代器模式后,遍历图书馆的所有书籍,客户端完全不知道图书馆是如何存储书籍的。
四、总结
采用迭代器模式的思路:
(1)理解需求:如果自定义了一个较复杂结构,客户端又需要遍历这个复杂结构的元素,这时候要想到使用迭代器模式。
(2)复杂结构要实现Iterator结构:
public class ComplexStructure implements Iterator<Element> { @Override public Iterator<Element> iterator() { return new ComplexStructureIterator(this); } private class ComplexStructureIterator implements Iterator<Element> { private ComplexStructure complexStructure; public ComplexStructureIterator(ComplexStructure complexStructure) { this.complexStructure = complexStructure; } @Override public boolean hasNext() { // 待实现 } @Override public Element next() { // 待实现 } } }
(3)客户端使用迭代器:
Iterator<Element> iterator = library.iterator(); while (iterator.hasNext()) { Element element = iterator.next(); ... }