3.4. 迭代器模式
3.4.1. 目的
使一个对象可迭代,并使它看起来像对象集合。
3.4.2. Examples
通过遍历文件的所有行(对象表现形式的)来逐行处理文件(也是对象)
3.4.3. 注意
标准PHP库(SPL)定义了一个最适合这种模式的接口迭代器! 通常也要实现Countable接口,允许在iterable对象上使用count($object)
3.4.4. UML 图

3.4.5. 代码
在 GitHub 上查看代码
Book.php
1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\Behavioral\Iterator;
6
7class Book
8{
9 public function __construct(private string $title, private string $author)
10 {
11 }
12
13 public function getAuthor(): string
14 {
15 return $this->author;
16 }
17
18 public function getTitle(): string
19 {
20 return $this->title;
21 }
22
23 public function getAuthorAndTitle(): string
24 {
25 return $this->getTitle() . ' by ' . $this->getAuthor();
26 }
27}
BookList.php
1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\Behavioral\Iterator;
6
7use Countable;
8use Iterator;
9
10class BookList implements Countable, Iterator
11{
12 /**
13 * @var Book[]
14 */
15 private array $books = [];
16 private int $currentIndex = 0;
17
18 public function addBook(Book $book)
19 {
20 $this->books[] = $book;
21 }
22
23 public function removeBook(Book $bookToRemove)
24 {
25 foreach ($this->books as $key => $book) {
26 if ($book->getAuthorAndTitle() === $bookToRemove->getAuthorAndTitle()) {
27 unset($this->books[$key]);
28 }
29 }
30
31 $this->books = array_values($this->books);
32 }
33
34 public function count(): int
35 {
36 return count($this->books);
37 }
38
39 public function current(): Book
40 {
41 return $this->books[$this->currentIndex];
42 }
43
44 public function key(): int
45 {
46 return $this->currentIndex;
47 }
48
49 public function next(): void
50 {
51 $this->currentIndex++;
52 }
53
54 public function rewind(): void
55 {
56 $this->currentIndex = 0;
57 }
58
59 public function valid(): bool
60 {
61 return isset($this->books[$this->currentIndex]);
62 }
63}
3.4.6. 测试
Tests/IteratorTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\Behavioral\Iterator\Tests;
6
7use DesignPatterns\Behavioral\Iterator\Book;
8use DesignPatterns\Behavioral\Iterator\BookList;
9use PHPUnit\Framework\TestCase;
10
11class IteratorTest extends TestCase
12{
13 public function testCanIterateOverBookList()
14 {
15 $bookList = new BookList();
16 $bookList->addBook(new Book('Learning PHP Design Patterns', 'William Sanders'));
17 $bookList->addBook(new Book('Professional Php Design Patterns', 'Aaron Saray'));
18 $bookList->addBook(new Book('Clean Code', 'Robert C. Martin'));
19
20 $books = [];
21
22 foreach ($bookList as $book) {
23 $books[] = $book->getAuthorAndTitle();
24 }
25
26 $this->assertSame(
27 [
28 'Learning PHP Design Patterns by William Sanders',
29 'Professional Php Design Patterns by Aaron Saray',
30 'Clean Code by Robert C. Martin',
31 ],
32 $books
33 );
34 }
35
36 public function testCanIterateOverBookListAfterRemovingBook()
37 {
38 $book = new Book('Clean Code', 'Robert C. Martin');
39 $book2 = new Book('Professional Php Design Patterns', 'Aaron Saray');
40
41 $bookList = new BookList();
42 $bookList->addBook($book);
43 $bookList->addBook($book2);
44 $bookList->removeBook($book);
45
46 $books = [];
47 foreach ($bookList as $book) {
48 $books[] = $book->getAuthorAndTitle();
49 }
50
51 $this->assertSame(
52 ['Professional Php Design Patterns by Aaron Saray'],
53 $books
54 );
55 }
56
57 public function testCanAddBookToList()
58 {
59 $book = new Book('Clean Code', 'Robert C. Martin');
60
61 $bookList = new BookList();
62 $bookList->addBook($book);
63
64 $this->assertCount(1, $bookList);
65 }
66
67 public function testCanRemoveBookFromList()
68 {
69 $book = new Book('Clean Code', 'Robert C. Martin');
70
71 $bookList = new BookList();
72 $bookList->addBook($book);
73 $bookList->removeBook($book);
74
75 $this->assertCount(0, $bookList);
76 }
77}