迭代器模式(Iterator)
前言
迭代器模式提供一种方法顺序访问聚合对象中的元素,而不暴露其内部表示。它是现代编程语言中最普及的设计模式之一,for...of、Array.forEach 等都是迭代器的应用。本文将详细讲解迭代器模式的核心原理及实际应用。
一、核心概念
1.1 定义
提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其对象的内部表示。
1.2 核心角色
| 角色 | 说明 |
|---|---|
| 迭代器接口(Iterator) | 定义遍历元素的方法 |
| 具体迭代器(ConcreteIterator) | 实现遍历逻辑 |
| 聚合接口(Aggregate) | 定义创建迭代器的方法 |
| 具体聚合(ConcreteAggregate) | 实现创建迭代器 |
1.3 迭代器类型
| 类型 | 说明 |
|---|---|
| 内部迭代器 | 迭代器自己控制遍历(如 forEach) |
| 外部迭代器 | 客户端控制遍历(如 next()) |
| 生成器迭代器 | 使用 yield 生成的迭代器 |
二、代码实现
2.1 基础实现
php
<?php
// PHP 内置 Iterator 接口(PHP 自带,无需自定义)
// interface Iterator extends Traversable {
// public function current(): mixed;
// public function key(): mixed;
// public function next(): void;
// public function rewind(): void;
// public function valid(): bool;
// }
// 聚合接口
interface Aggregate
{
public function createIterator(): Iterator;
}
// 具体迭代器:实现 PHP 内置 Iterator 接口
class ConcreteIterator implements Iterator
{
private array $collection;
private int $index = 0;
public function __construct(array $collection)
{
$this->collection = array_values($collection);
}
public function current(): mixed
{
return $this->collection[$this->index] ?? null;
}
public function next(): void
{
$this->index++;
}
public function key(): mixed
{
return $this->index;
}
public function valid(): bool
{
return isset($this->collection[$this->index]);
}
public function rewind(): void
{
$this->index = 0;
}
}
// 具体聚合
class ConcreteCollection implements Aggregate
{
/** @var mixed[] */
private array $items = [];
public function addItem($item): void
{
$this->items[] = $item;
}
public function createIterator(): Iterator
{
return new ConcreteIterator($this->items);
}
}
// 使用
$collection = new ConcreteCollection();
$collection->addItem("Item 1");
$collection->addItem("Item 2");
$collection->addItem("Item 3");
$iterator = $collection->createIterator();
foreach ($iterator as $item) {
echo $item . PHP_EOL;
}2.2 实际案例:自定义树遍历
php
<?php
// 树节点
class TreeNode
{
public string $value;
/** @var TreeNode[] */
public array $children = [];
public function __construct(string $value)
{
$this->value = $value;
}
public function add(TreeNode $child): self
{
$this->children[] = $child;
return $this;
}
}
// 深度优先迭代器
class DFSIterator implements Iterator
{
/** @var TreeNode[] */
private array $stack;
private int $index = 0;
/** @var TreeNode[] */
private array $result;
public function __construct(TreeNode $root)
{
$this->stack = [$root];
$this->result = [];
// 一次性生成快照结果
while (!empty($this->stack)) {
$node = array_pop($this->stack);
$this->result[] = $node;
// 逆序压入子节点(保证从左到右遍历)
for ($i = count($node->children) - 1; $i >= 0; $i--) {
$this->stack[] = $node->children[$i];
}
}
}
public function current(): mixed
{
return $this->result[$this->index] ?? null;
}
public function next(): void
{
$this->index++;
}
public function key(): mixed
{
return $this->index;
}
public function valid(): bool
{
return isset($this->result[$this->index]);
}
public function rewind(): void
{
$this->index = 0;
}
}
// 广度优先迭代器
class BFSIterator implements Iterator
{
/** @var TreeNode[] */
private array $result;
private int $index = 0;
public function __construct(TreeNode $root)
{
$queue = [$root];
$this->result = [];
while (!empty($queue)) {
$node = array_shift($queue);
$this->result[] = $node;
foreach ($node->children as $child) {
$queue[] = $child;
}
}
}
public function current(): mixed
{
return $this->result[$this->index] ?? null;
}
public function next(): void
{
$this->index++;
}
public function key(): mixed
{
return $this->index;
}
public function valid(): bool
{
return isset($this->result[$this->index]);
}
public function rewind(): void
{
$this->index = 0;
}
}
// 使用
$tree = (new TreeNode("Root"))
->add((new TreeNode("A"))->add(new TreeNode("A1"))->add(new TreeNode("A2")))
->add((new TreeNode("B"))->add(new TreeNode("B1")));
echo "=== DFS ===" . PHP_EOL;
foreach (new DFSIterator($tree) as $node) {
echo $node->value . PHP_EOL;
}
// Root → A → A1 → A2 → B → B1
echo "=== BFS ===" . PHP_EOL;
foreach (new BFSIterator($tree) as $node) {
echo $node->value . PHP_EOL;
}
// Root → A → B → A1 → A2 → B12.3 使用 PHP Generator
php
<?php
// 使用 PHP Generator 实现迭代器(PHP 5.5+)
class NumberRange
{
public function __construct(
private int $start,
private int $end
) {}
// 生成器:正向遍历
public function forward(): Generator
{
for ($i = $this->start; $i <= $this->end; $i++) {
yield $i;
}
}
// 生成器:反向遍历
public function backward(): Generator
{
for ($i = $this->end; $i >= $this->start; $i--) {
yield $i;
}
}
// 生成器:步长遍历
public function step(int $step): Generator
{
for ($i = $this->start; $i <= $this->end; $i += $step) {
yield $i;
}
}
}
// 使用 foreach 遍历
$range = new NumberRange(1, 10);
echo "Forward:" . PHP_EOL;
foreach ($range->forward() as $num) {
echo $num . PHP_EOL;
}
echo "Step by 2:" . PHP_EOL;
foreach ($range->step(2) as $num) {
echo $num . PHP_EOL;
}三、适用场景
| 场景 | 说明 |
|---|---|
| 集合遍历 | 数组、链表、树等 |
| 统一遍历接口 | 不同数据结构统一遍历 |
| 多种遍历方式 | DFS、BFS、正序、倒序 |
| 懒加载 | 按需生成元素 |
| 无限序列 | 斐波那契数列等 |
| 分页数据 | 逐页加载 |
四、优缺点分析
| 优点 | 缺点 |
|---|---|
| 统一遍历接口 | 需要额外的迭代器类 |
| 简化集合操作 | 增加代码复杂度 |
| 支持多种遍历 | 简单集合过度设计 |
| 不暴露内部结构 | 遍历时修改集合可能出错 |
| 支持懒加载 | 需要管理迭代状态 |
五、常见踩坑与问题排查
5.1 遍历时修改集合
php
<?php
// 问题:遍历时修改集合导致错误
$list = [1, 2, 3, 4, 5];
foreach ($list as $item) {
if ($item === 3) {
$index = array_search($item, $list);
if ($index !== false) array_splice($list, $index, 1); // 危险!
}
}
// 解决:先收集要删除的元素,遍历后再删除
$toRemove = [];
foreach ($list as $item) {
if ($item === 3) $toRemove[] = $item;
}
foreach ($toRemove as $item) {
$index = array_search($item, $list);
if ($index !== false) array_splice($list, $index, 1);
}5.2 迭代器失效
php
<?php
// 问题:集合修改后,旧迭代器状态可能失效
$collection = new ConcreteCollection();
$collection->addItem(1);
$collection->addItem(2);
$iter = $collection->createIterator();
$collection->addItem(3); // 修改后迭代器可能不包含新元素
// 解决:使用快照迭代器
class SnapshotIterator implements Iterator
{
private array $snapshot;
private int $index = 0;
public function __construct(array $collection)
{
$this->snapshot = array_values($collection); // 快照
}
public function current(): mixed
{
return $this->snapshot[$this->index] ?? null;
}
public function next(): void
{
$this->index++;
}
public function key(): mixed
{
return $this->index;
}
public function valid(): bool
{
return isset($this->snapshot[$this->index]);
}
public function rewind(): void
{
$this->index = 0;
}
}六、优化方案与进阶
6.1 PHP IteratorAggregate 接口
php
<?php
// 实现 IteratorAggregate 使对象可被 foreach 遍历
class Range implements IteratorAggregate
{
public function __construct(
private int $start,
private int $end
) {}
public function getIterator(): Iterator
{
$current = $this->start;
$end = $this->end;
return new class($current, $end) implements Iterator {
private int $current;
private int $end;
private int $key = 0;
public function __construct(int $start, int $end)
{
$this->current = $start;
$this->end = $end;
}
public function current(): mixed
{
return $this->current;
}
public function next(): void
{
$this->current++;
$this->key++;
}
public function key(): mixed
{
return $this->key;
}
public function valid(): bool
{
return $this->current <= $this->end;
}
public function rewind(): void
{
$this->current = $this->start ?? $this->end;
$this->key = 0;
}
};
}
}
// 使用
$range = new Range(1, 5);
foreach ($range as $num) {
echo $num . PHP_EOL;
}6.2 无限序列
php
<?php
// 斐波那契数列(无限序列)
function fibonacci(): Generator
{
[$a, $b] = [0, 1];
while (true) {
yield $a;
[$a, $b] = [$b, $a + $b];
}
}
// 使用:取前 10 个
$fib = fibonacci();
for ($i = 0; $i < 10; $i++) {
echo $fib->current() . PHP_EOL;
$fib->next();
}
// 0, 1, 1, 2, 3, 5, 8, 13, 21, 34七、全文总结
迭代器模式的核心是 顺序访问聚合元素,不暴露内部结构。
核心要点:
- 迭代器将遍历逻辑从集合中分离
- 支持多种遍历方式(DFS、BFS、正序、倒序)
- ES6 Generator 简化了迭代器实现
- 实现
Symbol.iterator可使对象支持for...of - 注意遍历时修改集合的问题,可使用快照解决
