一、Iterator(迭代器)设计模式

1、示例场景

将多本书(Book)放入书架(BookShelf),然后通过 迭代器(Iterator 遍历并打印每本书的名称。

该示例展示了如何使用 Iterator 模式 实现对集合元素的安全、统一遍历,同时 解耦遍历逻辑与集合内部实现

2、UML

1558680180085

3、代码实现

1)Aggregate 接口:集合抽象(相当于 Iterable

/**
 * 集合接口(Aggregate)
 * 定义创建迭代器的方法
 */
public interface Aggregate {
    Iterator iterator();
}

2)Iterator 接口:迭代器抽象

/**
 * 迭代器接口
 * 提供统一的遍历方式
 */
public interface Iterator {
    boolean hasNext(); // 是否还有下一个元素
    Object next();     // 返回下一个元素
}

3)Book 类:数据模型

import lombok.Data;
import lombok.AllArgsConstructor;

@Data
@AllArgsConstructor
public class Book {
    private String name;
}

4)BookShelf 类:具体集合(ConcreteAggregate

import java.util.ArrayList;
import java.util.List;

/**
 * 书架类 —— 具体的聚合体(ConcreteAggregate)
 * 内部使用 List 存储书籍,但对外隐藏实现细节
 */
public class BookShelf implements Aggregate {

    private final List<Book> books = new ArrayList<>();
    
    // 可选:用 size 记录数量(也可直接用 books.size())
    private int size = 0;

    public void appendBook(Book book) {
        books.add(book);
        size++;
    }

    public Book getBookAt(int index) {
        return books.get(index);
    }

    public int getLength() {
        return size; // 或 return books.size();
    }

    @Override
    public Iterator iterator() {
        return new BookShelfIterator(this);
    }
}

5)BookShelfIterator 类:具体迭代器(ConcreteIterator

/**
 * 书架迭代器 —— 具体的迭代器(ConcreteIterator)
 */
public class BookShelfIterator implements Iterator {

    private final BookShelf bookShelf;
    private int index = 0;

    public BookShelfIterator(BookShelf bookShelf) {
        this.bookShelf = bookShelf;
    }

    @Override
    public boolean hasNext() {
        return index < bookShelf.getLength();
    }

    @Override
    public Object next() {
        if (!hasNext()) {
            throw new IllegalStateException("No more elements");
        }
        Book book = bookShelf.getBookAt(index);
        index++; // 移动到下一个位置
        return book;
    }
}

6) 测试类 Main

public class Main {
    public static void main(String[] args) {
        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 it = bookShelf.iterator();
        while (it.hasNext()) {
            Book book = (Book) it.next();
            System.out.println(book.getName());
        }
    }
}

4、Iterator 模式中的角色

角色 说明 示例
Aggregate(抽象集合) 定义创建迭代器的接口 Aggregate 接口
ConcreteAggregate(具体集合) 实现 Aggregate,提供具体迭代器 BookShelf
Iterator(抽象迭代器) 定义遍历接口(hasNext, next Iterator 接口
ConcreteIterator(具体迭代器) 实现遍历逻辑,持有对集合的引用 BookShelfIterator

1558683119564

5、FQA

1)为什么需要 Iterator?—— 核心价值

答案:解耦遍历逻辑与集合实现

  • 客户端代码 只依赖 Iterator 接口,不关心集合是 ArrayList、数组、链表还是自定义结构。
  • 即使 BookShelf 内部从 List 改为 普通数组树结构远程数据库查询,只要 iterator() 方法返回合法的 Iterator客户端代码无需任何修改
// 客户端代码完全不变!
Iterator it = bookShelf.iterator();
while (it.hasNext()) {
    Book book = (Book) it.next();
    System.out.println(book.getName());
}

这就是“面向接口编程”和“开闭原则”的体现:对扩展开放,对修改关闭。

2)误区:next() 的语义

  • next() 返回的是“当前”元素,然后 移动指针到下一个位置
  • 初学者常误以为 next() 返回“下一个”元素(其实是指针移动后的“新当前”)。
初始: index = 0
调用 next()  返回 books[0]index 变为 1
再调用 next()  返回 books[1]index 变为 2
...

3)误区:缺少边界检查

  • 应在 next() 中校验是否还有元素,避免越界。
  • 最佳实践:先调用 hasNext(),再调用 next()

4)误区:类型安全问题

  • 当前使用 Object 返回值,需强制类型转换,存在风险。
  • 优化建议:使用 泛型 提升类型安全(见下文)。
public interface Iterator<T> {
    boolean hasNext();
    T next();
}

public interface Aggregate<T> {
    Iterator<T> iterator();
}

public class BookShelfIterator implements Iterator<Book> { ... }

public class BookShelf implements Aggregate<Book> { ... }
Iterator<Book> it = bookShelf.iterator();
while (it.hasNext()) {
    Book book = it.next(); // 无需强制转换!
    System.out.println(book.getName());
}

3、扩展思路

Book数组我们直接for循环打印出来不久好了,为啥还要使用Iterator这个东西呢 ,

原因:引入Iterator后可以讲遍历与实现分离开来 ,不管实现如何变化,都可以使用Iterator,这里只使用了Iterator的hasNext方法和next方法,并没有调用BookShelf方法,也就是说While循环不依赖BookShelf的实现

举例:如果编写BookShel 书架的开发人员觉得不使用List集合来管理书本,而是使用其他的,比如使用数据来管理书本,,这样的话,不管书架 BookShel如何变化,只要BootShelf的iterator的方法能够正确返回Iterator的实例 即使不对while做任何修改,代码都可以正常工作。

Iterator it = bookShelf.iterator();
while (it.hasNext()) {
    Book book = (Book)it.next();
    System.out.println(book.getName());
}

4、容易出错的地方

4.1、容易容错下一个,很容易在next方法上出错, next方法是,返回当前的元素,index指向下一个元素