首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 設計模式:迭代器模式完整實作與 SPL 應用

     編輯:PHP 綜合知識

迭代器模式:統一存取集合元素的標準化介面

迭代器模式提供一致的方式遍歷不同資料結構 (陣列、鏈表、樹、資料庫結果集),隱藏內部實現細節。PHP 內建 Iterator、IteratorAggregate 介面與 SPL (Standard PHP Library) 提供豐富迭代器。

核心介面定義

<?php
// PHP 內建介面 (PHP 5+)
interface Iterator extends Traversable {
    public function current(): mixed;     // 當前元素
    public function next(): void;         // 移至下一個
    public function key(): mixed;         // 當前鍵值
    public function valid(): bool;        // 是否有效
    public function rewind(): void;       // 重置至第一個
}

interface IteratorAggregate extends Traversable {
    public function getIterator(): Traversable; // 回傳 Iterator 實例
}
?>

手工實作:圖書館藏書迭代器

<?php
// 1. 領域物件
class Book {
    public function __construct(
        public string $title,
        public string $author,
        public int $year
    ) {}
}

// 2. 集合類別:實作 IteratorAggregate
class Library implements IteratorAggregate {
    private array $books = [];
    
    public function addBook(Book $book): void {
        $this->books[] = $book;
    }
    
    public function getIterator(): Traversable {
        // 回傳內建 ArrayIterator (已實作 Iterator)
        return new ArrayIterator($this->books);
    }
    
    // 進階:自訂過濾迭代器
    public function getBooksAfterYear(int $year): Traversable {
        return new CallbackFilterIterator(
            new ArrayIterator($this->books),
            fn($book) => $book->year > $year
        );
    }
}

// 3. 使用範例
$library = new Library();
$library->addBook(new Book("Design Patterns", "GoF", 1994));
$library->addBook(new Book("Clean Code", "Robert Martin", 2008));
$library->addBook(new Book("PHP 8 in Action", "Author", 2023));

// 基本遍歷
foreach ($library as $book) {
    echo "{$book->title} ({$book->year})\n";
}

// 過濾遍歷
echo "\n2000 年後出版:\n";
foreach ($library->getBooksAfterYear(2000) as $book) {
    echo "{$book->title} ({$book->year})\n";
}
?>

SPL 內建迭代器大全

迭代器 用途 範例
ArrayIterator 陣列遍歷 new ArrayIterator([1,2,3])
DirectoryIterator 目錄掃描 new DirectoryIterator('/path')
RecursiveDirectoryIterator 遞迴目錄掃描 new RecursiveDirectoryIterator('/path')
FilesystemIterator 檔案系統 (跳過 . ..) new FilesystemIterator('/path')
GlobIterator Glob 模式匹配 new GlobIterator('*.php')
XMLReader XML 流式解析 $r = new XMLReader(); $r->open('file.xml');
PDOStatement 資料庫結果集 $stmt->fetchAll() 但可直接 foreach($stmt as $row)
SplFileObject 檔案逐行讀取 new SplFileObject('data.csv')

過濾與轉換迭代器 (裝飾器模式應用)

<?php
$numbers = new ArrayIterator(range(1, 20));

// 1. 過濾:只保留偶數
$even = new CallbackFilterIterator($numbers, fn($v) => $v % 2 === 0);

// 2. 映射:平方
$squared = new CallbackFilterIterator($even, fn($v) => $v * $v); // 錯誤:Filter 不能映射
// 正確用 IteratorIterator 包裝或用 Generator

// 正確映射方式:Generator (PHP 5.5+)
function map(Traversable $src, callable $fn): Generator {
    foreach ($src as $k => $v) yield $k => $fn($v);
}

$squared = map($even, fn($v) => $v * $v);

// 3. 限制:前 5 個
$limited = new LimitIterator($squared, 0, 5);

foreach ($limited as $v) {
    echo "$v\n"; // 4, 16, 36, 64, 100
}
?>

自訂迭代器:資料庫分頁迭代器

<?php
class PaginatedResult implements Iterator {
    private PDOStatement $stmt;
    private int $pageSize;
    private int $currentPage = 0;
    private array $currentPageData = [];
    private int $currentIndex = 0;
    private bool $fetchedAll = false;
    
    public function __construct(PDO $pdo, string $sql, int $pageSize = 100) {
        $this->stmt = $pdo->prepare($sql);
        $this->stmt->execute();
        $this->pageSize = $pageSize;
    }
    
    public function rewind(): void {
        $this->currentPage = 0;
        $this->currentIndex = 0;
        $this->currentPageData = [];
        $this->fetchedAll = false;
        $this->fetchNextPage();
    }
    
    public function current(): mixed {
        return $this->currentPageData[$this->currentIndex] ?? null;
    }
    
    public function key(): mixed {
        return $this->currentPage * $this->pageSize + $this->currentIndex;
    }
    
    public function next(): void {
        $this->currentIndex++;
        if ($this->currentIndex >= count($this->currentPageData)) {
            $this->fetchNextPage();
        }
    }
    
    public function valid(): bool {
        return !$this->fetchedAll && $this->currentIndex < count($this->currentPageData);
    }
    
    private function fetchNextPage(): void {
        $this->currentPageData = $this->stmt->fetchAll(PDO::FETCH_ASSOC);
        if (empty($this->currentPageData)) {
            $this->fetchedAll = true;
        }
        $this->currentIndex = 0;
        $this->currentPage++;
    }
}

// 使用:記憶體效率處理百萬筆資料
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$iterator = new PaginatedResult($pdo, "SELECT * FROM huge_table");

foreach ($iterator as $row) {
    // 處理每筆資料,記憶體只佔用一頁大小
    process($row);
}
?>

Generator:輕量級迭代器 (PHP 5.5+)

<?php
// 斐波那契數列 Generator
function fibonacci(int $limit): Generator {
    $a = 0; $b = 1;
    for ($i = 0; $i < $limit; $i++) {
        yield $a;
        [$a, $b] = [$b, $a + $b];
    }
}

foreach (fibonacci(10) as $n) {
    echo "$n "; // 0 1 1 2 3 5 8 13 21 34
}

// 檔案逐行處理 (記憶體友善)
function readLines(string $filename): Generator {
    $fh = fopen($filename, 'r');
    while (($line = fgets($fh)) !== false) {
        yield rtrim($line);
    }
    fclose($fh);
}

foreach (readLines('huge.log') as $line) {
    if (str_contains($line, 'ERROR')) {
        echo "Found: $line\n";
    }
}
?>

迭代器模式優缺點總結

優點 缺點
統一遍歷介面,客戶端不依賴具體集合類別 簡單陣列遍歷引入額外複雜度
支援多種遍歷方式 (正向、反向、過濾、分頁) 修改集合結構時迭代器可能失效
延遲載入、記憶體效率 (Generator、分頁迭代器) 並發修改需額外同步機制
符合單一職責:遍歷邏輯與業務邏輯分離
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved