迭代器模式(Iterator)

目的

让对象变得可迭代并表现得像对象集合。

例子

  • 在文件中的所有行(对象表示形式的)上逐行处理文件(也是对象)

注意

PHP 标准库 (SPL) 定义了一个最适合此模式的接口迭代器!往往也需要实现 Countable 接口,允许在迭代器对象上使用 count($object) 方法。

UML 类图

file

代码

你可以在 GitHub 上找到这些代码

Book.php

  1. <?php
  2. namespace DesignPatterns\Behavioral\Iterator;
  3. class Book
  4. {
  5. /**
  6. * @var string
  7. */
  8. private $author;
  9. /**
  10. * @var string
  11. */
  12. private $title;
  13. public function __construct(string $title, string $author)
  14. {
  15. $this->author = $author;
  16. $this->title = $title;
  17. }
  18. public function getAuthor(): string
  19. {
  20. return $this->author;
  21. }
  22. public function getTitle(): string
  23. {
  24. return $this->title;
  25. }
  26. public function getAuthorAndTitle(): string
  27. {
  28. return $this->getTitle().' by '.$this->getAuthor();
  29. }
  30. }

BookList.php

  1. <?php
  2. namespace DesignPatterns\Behavioral\Iterator;
  3. class BookList implements \Countable, \Iterator
  4. {
  5. /**
  6. * @var Book[]
  7. */
  8. private $books = [];
  9. /**
  10. * @var int
  11. */
  12. private $currentIndex = 0;
  13. public function addBook(Book $book)
  14. {
  15. $this->books[] = $book;
  16. }
  17. public function removeBook(Book $bookToRemove)
  18. {
  19. foreach ($this->books as $key => $book) {
  20. if ($book->getAuthorAndTitle() === $bookToRemove->getAuthorAndTitle()) {
  21. unset($this->books[$key]);
  22. }
  23. }
  24. $this->books = array_values($this->books);
  25. }
  26. public function count(): int
  27. {
  28. return count($this->books);
  29. }
  30. public function current(): Book
  31. {
  32. return $this->books[$this->currentIndex];
  33. }
  34. public function key(): int
  35. {
  36. return $this->currentIndex;
  37. }
  38. public function next()
  39. {
  40. $this->currentIndex++;
  41. }
  42. public function rewind()
  43. {
  44. $this->currentIndex = 0;
  45. }
  46. public function valid(): bool
  47. {
  48. return isset($this->books[$this->currentIndex]);
  49. }
  50. }

测试

Tests/IteratorTest.php

  1. <?php
  2. namespace DesignPatterns\Behavioral\Iterator\Tests;
  3. use DesignPatterns\Behavioral\Iterator\Book;
  4. use DesignPatterns\Behavioral\Iterator\BookList;
  5. use DesignPatterns\Behavioral\Iterator\BookListIterator;
  6. use DesignPatterns\Behavioral\Iterator\BookListReverseIterator;
  7. use PHPUnit\Framework\TestCase;
  8. class IteratorTest extends TestCase
  9. {
  10. public function testCanIterateOverBookList()
  11. {
  12. $bookList = new BookList();
  13. $bookList->addBook(new Book('Learning PHP Design Patterns', 'William Sanders'));
  14. $bookList->addBook(new Book('Professional Php Design Patterns', 'Aaron Saray'));
  15. $bookList->addBook(new Book('Clean Code', 'Robert C. Martin'));
  16. $books = [];
  17. foreach ($bookList as $book) {
  18. $books[] = $book->getAuthorAndTitle();
  19. }
  20. $this->assertEquals(
  21. [
  22. 'Learning PHP Design Patterns by William Sanders',
  23. 'Professional Php Design Patterns by Aaron Saray',
  24. 'Clean Code by Robert C. Martin',
  25. ],
  26. $books
  27. );
  28. }
  29. public function testCanIterateOverBookListAfterRemovingBook()
  30. {
  31. $book = new Book('Clean Code', 'Robert C. Martin');
  32. $book2 = new Book('Professional Php Design Patterns', 'Aaron Saray');
  33. $bookList = new BookList();
  34. $bookList->addBook($book);
  35. $bookList->addBook($book2);
  36. $bookList->removeBook($book);
  37. $books = [];
  38. foreach ($bookList as $book) {
  39. $books[] = $book->getAuthorAndTitle();
  40. }
  41. $this->assertEquals(
  42. ['Professional Php Design Patterns by Aaron Saray'],
  43. $books
  44. );
  45. }
  46. public function testCanAddBookToList()
  47. {
  48. $book = new Book('Clean Code', 'Robert C. Martin');
  49. $bookList = new BookList();
  50. $bookList->addBook($book);
  51. $this->assertCount(1, $bookList);
  52. }
  53. public function testCanRemoveBookFromList()
  54. {
  55. $book = new Book('Clean Code', 'Robert C. Martin');
  56. $bookList = new BookList();
  57. $bookList->addBook($book);
  58. $bookList->removeBook($book);
  59. $this->assertCount(0, $bookList);
  60. }
  61. }