迭代器模式:让复杂对象也能优雅地“走个过场” 说到遍历数据,PHP开发者最熟悉的莫过
说到遍历数据,PHP开发者最熟悉的莫过于数组和那个万能的foreach了。就像下面这样,简单直接:
免费影视、动漫、音乐、游戏、小说资源长期稳定更新! 👉 点此立即查看 👈
$users = ['Alice', 'Bob', 'Charlie'];foreach ($users as $user) { ... }
但问题来了,如果你的数据并非老老实实地待在一个数组里,而是被封装在一个复杂的对象内部呢?举个例子,一个Department(部门)对象,它内部维护着一个employees(员工)数组,并且这个数组是私有的。
// ❌ 无法直接遍历class Department{ private array $employees = []; public function getEmployees(): array { return $this->employees; }}$dept = new Department();// foreach ($dept as $emp) { ... } // 报错!或者无法访问私有属性
为了能遍历,你很可能被迫公开getEmployees()方法,把内部数组直接暴露出去。这埋下了一个隐患:如果未来某天,你决定把底层的数据结构从“数组”改成“链表”甚至“树”,那么所有调用了getEmployees()的客户端代码都将面临崩溃。
这正是迭代器模式要解决的问题。它提供了一种方法,让你能顺序访问一个聚合对象中的各个元素,同时又无需暴露该对象的内部表示。简单说,它让你的自定义对象也能支持foreach,并且你还能完全掌控遍历的逻辑——比如倒着来,或者跳过某些元素。

其实,PHP早就为我们准备好了强大的内置武器:Iterator和IteratorAggregate接口。这里有个业界共识:使用IteratorAggregate配合生成器(Generator),是目前PHP中实现迭代器最优雅、最高效的方式,没有之一。
我们不想让外部直接接触到书架内部的$books数组,而是通过迭代器来访问。
books[] = $book; } // ⚡️ 核心:实现 getIterator 方法 // 这里我们使用 yield 关键字,瞬间生成一个迭代器 publicfunction getIterator(): Tra versable { // 可以在这里控制遍历顺序,比如倒序 for ($i = count($this->books) - 1; $i >= 0; $i--) { yield$this->books[$i]; } // 或者简单的: // yield from $this->books; }}
现在,Bookshelf对象可以直接被foreach遍历了。最关键的是,外部调用者完全不知道你内部是用数组、链表还是其他什么数据结构存储的。
$shelf = new Bookshelf();$shelf->addBook(new Book("设计模式之禅"));$shelf->addBook(new Book("PHP 高级编程"));$shelf->addBook(new Book("重构"));// 直接遍历对象!echo"书架上的书(倒序):\n";foreach ($shelf as $book) { echo"? {$book->title}\n";}// 输出:// ? 重构// ? PHP 高级编程// ? 设计模式之禅
PHP的SPL(标准PHP库)里其实藏着一变钱成的迭代器工具箱,可惜很多开发者从未留意:
ArrayIterator: 遍历数组。DirectoryIterator: 遍历文件目录。FilterIterator: 过滤掉不想要的元素。LimitIterator: 只遍历前 N 个元素(实现分页的利器)。InfiniteIterator: 无限循环遍历。你可以像搭积木一样把它们组合起来使用,实现复杂而清晰的遍历逻辑:
// 遍历目录,过滤出 .php 文件,并且只取前 5 个$iterator = new DirectoryIterator(__DIR__);$filter = new CallbackFilterIterator($iterator, fn($file) => $file->getExtension() === 'php');$limit = new LimitIterator($filter, 0, 5);foreach ($limit as $file) { echo $file->getFilename() . "\n";}
yield逐行读取(生成器本质就是迭代器)是唯一的选择。关于迭代器模式,我们可以这样理解:
最后,留一个延伸思考:在PHP中,当你用foreach遍历一个没有实现任何迭代器接口的对象时,PHP默认会遍历该对象的所有公有(public)属性。这算不算一种原生的迭代器模式呢?它又有什么局限性?(提示:它无法遍历私有属性,你也完全无法控制遍历的逻辑和顺序。)
菜鸟下载发布此文仅为传递信息,不代表菜鸟下载认同其观点或证实其描述。