PHP设计模式 PHP 迭代器模式

2024-02-26 开发教程 PHP设计模式 匿名 2

目的

使一个对象可迭代,并使它看起来像对象集合。

例子

  • 通过遍历文件的所有行(对象表现形式的)来逐行处理文件(也是对象)

注意

标准PHP库(SPL)定义了一个最适合这种模式的接口迭代器! 通常也要实现Countable接口,允许在iterable对象上使用count($object)

UML 图

代码

在 GitHub 上查看代码

Book.php

<?php
declare(strict_types=1);
namespace DesignPatterns\Behavioral\Iterator;
class Book
{
public function __construct(private string $title, private string $author)
{
}
public function getAuthor(): string
{
return $this->author;
}
public function getTitle(): string
{
return $this->title;
}
public function getAuthorAndTitle(): string
{
return $this->getTitle() . ' by ' . $this->getAuthor();
}
}

BookList.php

<?php
declare(strict_types=1);
namespace DesignPatterns\Behavioral\Iterator;
use Countable;
use Iterator;
class BookList implements Countable, Iterator
{
/**
* @var Book[]
*/
private array $books = [];
private int $currentIndex = 0;
public function addBook(Book $book)
{
$this->books[] = $book;
}
public function removeBook(Book $bookToRemove)
{
foreach ($this->books as $key => $book) {
if ($book->getAuthorAndTitle() === $bookToRemove->getAuthorAndTitle()) {
unset($this->books[$key]);
}
}
$this->books = array_values($this->books);
}
public function count(): int
{
return count($this->books);
}
public function current(): Book
{
return $this->books[$this->currentIndex];
}
public function key(): int
{
return $this->currentIndex;
}
public function next()
{
$this->currentIndex++;
}
public function rewind()
{
$this->currentIndex = 0;
}
public function valid(): bool
{
return isset($this->books[$this->currentIndex]);
}
}

测试

Tests/IteratorTest.php

<?php
declare(strict_types=1);
namespace DesignPatterns\Behavioral\Iterator\Tests;
use DesignPatterns\Behavioral\Iterator\Book;
use DesignPatterns\Behavioral\Iterator\BookList;
use PHPUnit\Framework\TestCase;
class IteratorTest extends TestCase
{
public function testCanIterateOverBookList()
{
$bookList = new BookList();
$bookList->addBook(new Book('Learning PHP Design Patterns', 'William Sanders'));
$bookList->addBook(new Book('Professional Php Design Patterns', 'Aaron Saray'));
$bookList->addBook(new Book('Clean Code', 'Robert C. Martin'));
$books = [];
foreach ($bookList as $book) {
$books[] = $book->getAuthorAndTitle();
}
$this->assertSame(
[
'Learning PHP Design Patterns by William Sanders',
'Professional Php Design Patterns by Aaron Saray',
'Clean Code by Robert C. Martin',
],
$books
);
}
public function testCanIterateOverBookListAfterRemovingBook()
{
$book = new Book('Clean Code', 'Robert C. Martin');
$book2 = new Book('Professional Php Design Patterns', 'Aaron Saray');
$bookList = new BookList();
$bookList->addBook($book);
$bookList->addBook($book2);
$bookList->removeBook($book);
$books = [];
foreach ($bookList as $book) {
$books[] = $book->getAuthorAndTitle();
}
$this->assertSame(
['Professional Php Design Patterns by Aaron Saray'],
$books
);
}
public function testCanAddBookToList()
{
$book = new Book('Clean Code', 'Robert C. Martin');
$bookList = new BookList();
$bookList->addBook($book);
$this->assertCount(1, $bookList);
}
public function testCanRemoveBookFromList()
{
$book = new Book('Clean Code', 'Robert C. Martin');
$bookList = new BookList();
$bookList->addBook($book);
$bookList->removeBook($book);
$this->assertCount(0, $bookList);
}
}