Iteratorパターン とは ??
「Iterate」は「◯◯を繰り返す」という意味で、
プログラミングにおいては「for」や「while」などを用いた「反復処理をする」という意味で使われる。
「Iterator パターン」は、集約オブジェクト(コレクションオブジェクト)の中の要素を列挙する手段を提供して、具体的な列挙方法を集約オブジェクトから隠蔽することで、列挙方法を抽象化します。
とのこと
集約オブジェクトとは ??
何らかの「情報の集まり」を格納したオブジェクトのこと。
// 「書籍」集約オブジェクト
$book_list = [
'星の王子さま',
'銀河鉄道の夜',
'吾輩は猫である',
'西の魔女が死んだ',
'コーヒーが冷めないうちに'
];
集約オブジェクトをIterateする !!
foreachで回すだけ。
foreach ($book_list as $book) {
echo $book;
}
後日集約オブジェクトの$book_listの中身が変わって、下記になったとすると...
$book_list = [
[
'title' => '星の王子さま',
'author' => 'サン=テグジュペリ'
],
[
'title' => '銀河鉄道の夜',
'author' => '宮沢賢治'
],
[
'title' => '吾輩は猫である',
'author' => '夏目漱石'
],
[
'title' => '西の魔女が死んだ',
'author' => '梨木香歩'
],
[
'title' => 'コーヒーが冷めないうちに',
'author' => '川口俊和'
]
];
データの構造が変わったので、同じ出力をするためには以前に書いたコードを書き直さなければならない。
foreach ($book_list as $book_obj) {
foreach ($book_obj as $title => $author) {
echo $title . ' - ' . $author;
}
}
更にIDが追加されて、IDの昇順に並び替えたい ! となると...
$book_list = [
[
'title' => '星の王子さま',
'author' => 'サン=テグジュペリ',
'ID' => 3
],
[
'title' => '銀河鉄道の夜',
'author' => '宮沢賢治',
'ID' => 5
],
[
'title' => '吾輩は猫である',
'author' => '夏目漱石',
'ID' => 1
],
[
'title' => '西の魔女が死んだ',
'author' => '梨木香歩',
'ID' => 4
],
[
'title' => 'コーヒーが冷めないうちに',
'author' => '川口俊和',
'ID' => 2
]
また以前のコードの改修作業が必要になる。
これが、10箇所、20箇所で使っていたら全て置き換えるのは大変なので、
こういうときに「Iteratorパターン」を使用する!
どのように実装する ??
集約オブジェクトに「Iteratorオブジェクト」を提供させることで解決!
「Iteratorオブジェクト」は集約オブジェクトを列挙する方法を知っているので、
クライアント(Iteratorを使うオブジェクト)は実装の詳細を知る必要はなく、
「Iteratorオブジェクト」の使い方さえ知っていればよい 🙆⭕️
後からいくら集約オブジェクトのタイプが増えても、クライアント側の記述そのままでよくなるため、プログラムの柔軟性がぐっと上がる
<?php
/* -----------------------
* インターフェイス
* -----------------------*/
// Aggregate Interface
interface BookList {
/**
* Iteratorを返すメソッド
* @return BookIterator
*/
public function createIterator();
}
// Iterator Interface
interface BookIterator {
/**
* 次の要素があるかどうかを判定する
* @return bool
*/
public function hasNext();
/**
* 次の要素を返し、ポインターを進める
* @return mixed
*/
public function next();
}
/* -----------------------
* Iteratorの実装
* -----------------------*/
// Iteratorクラス、このクラス内で列挙の手順を記述する。
// BookIteratorインターフェイスを実装するようにする。
class BookList_Simple_Iterator implements BookIterator {
private $books;
private $position = 0;
function __construct($books) {
$this->books = $books;
}
public function hasNext() {
return isset($this->books[$this->position]);
}
public function next() {
return $this->books[$this->position++];
}
}
// 別のIteratorクラス、列挙の手順が一個前と違うけど、
// 同じBookIteratorインターフェイスを持っているので、使う側からしたら差異はない
class BookList_Detailed_Iterator implements BookIterator {
protected $books;
private $position = 0;
function __construct($books) {
$this->books = $books;
}
public function hasNext() {
return isset($this->books[$this->position]);
}
public function next() {
$book = $this->books[$this->position++];
$title = $book['title'];
$author = $book['author'];
return $title . ' - ' . $author;
}
}
// 更に別のIteratorクラス、「BookList_Detailed_Iterator」と似ているので、継承する
class BookList_Detailed_OrderByID_Iterator extends BookList_Detailed_Iterator implements BookIterator {
function __construct($books) {
usort($books, function($a, $b) {
return (int)$a['id'] > (int)$b['id'];
});
$this->books = $books;
}
}
/* -----------------------------
* Aggregate(集約オブジェクト)の実装
* -----------------------------*/
// クラス間で共有しているメソッドたちを分離しておく。
trait BookList_Methods {
private $book_list;
function __construct($books) {
$this->book_list = $books;
}
public function add_to_list($book) {
$this->book_list[] = $book;
}
public function get_book_list() {
return $this->book_list;
}
}
// 集約オブジェクト 書籍一覧[簡易版]
class Books_Simple implements BookList {
use BookList_Methods;
public function createIterator() {
return new BookList_Simple_Iterator($this->book_list);
}
}
// 集約オブジェクト 書籍一覧[詳細版]
class Books_Detailed implements BookList {
use BookList_Methods;
public function createIterator() {
return new BookList_Detailed_Iterator($this->book_list);
}
}
// 集約オブジェクト 書籍一覧[詳細+並び替え版]
class Books_Detailed_OrderByID implements BookList {
use BookList_Methods;
public function createIterator() {
return new BookList_Detailed_OrderByID_Iterator($this->book_list);
}
}
/* --------------------------
* クライアントである「書籍クラス」
* --------------------------*/
class Book {
/** @type BookIterator $book_iterator */
private $book_iterator;
function __construct(BookList $book_list) {
// Iteratorオブジェクトを持たせる
$this->book_iterator = $book_list->createIterator();
}
function print_books() {
// Iteratorオブジェクトを使う
while ($this->book_iterator->hasNext()) {
$book = $this->book_iterator->next();
echo $book . '<br>';
}
}
}
/* -----------------------
* メイン処理
* -----------------------*/
// 書籍一覧のデータ[詳細]
$books_detailed_array = [
[
'title' => '星の王子さま',
'author' => 'サン=テグジュペリ',
'ID' => 3
],
[
'title' => '銀河鉄道の夜',
'author' => '宮沢賢治',
'ID' => 5
],
[
'title' => '吾輩は猫である',
'author' => '夏目漱石',
'ID' => 1
],
[
'title' => '西の魔女が死んだ',
'author' => '梨木香歩',
'ID' => 4
],
[
'title' => 'コーヒーが冷めないうちに',
'author' => '川口俊和',
'ID' => 2
]
// 書籍一覧のデータ[簡易]
$books_simple_array = [
'星の王子さま',
'銀河鉄道の夜',
'吾輩は猫である',
'西の魔女が死んだ',
'コーヒーが冷めないうちに'
];
// それぞれ違う集約オブジェクトで書籍クラスをインスタンス化
$book_a = new Book(new Books_Detailed($books_detailed_array));
$book_b = new Book(new Books_Simple($books_simple_array));
$book_c = new Book(new Books_Detailed_OrderByID($books_detailed_array));
?>
<section>
<h1>book_a</h1>
<?php $book_a->print_books(); ?>
</section>
<section>
<h1>book_b</h1>
<?php $book_b->print_books(); ?>
</section>
<section>
<h1>book_c</h1>
<?php $book_c->print_books(); ?>
</section>
<!-- 出力 -->
book_a
星の王子さま - サン=テグジュペリ
銀河鉄道の夜 - 宮沢賢治
吾輩は猫である - 夏目漱石
西の魔女が死んだ - 梨木香歩
コーヒーが冷めないうちに - 川口俊和
book_b
星の王子さま
銀河鉄道の夜
吾輩は猫である
西の魔女が死んだ
コーヒーが冷めないうちに
book_c
吾輩は猫である - 夏目漱石
コーヒーが冷めないうちに - 川口俊和
星の王子さま - サン=テグジュペリ
西の魔女が死んだ - 梨木香歩
銀河鉄道の夜 - 宮沢賢治
メリット
-
集約オブジェクト内部のデータ構造を隠蔽することができるため、コードの柔軟性が向上する。
-
Iteratorオブジェクトを使用することで、コレクション内部の要素に簡単にアクセスすることができる。
-
Iteratorオブジェクトは、コレクション内の要素を順番に処理するため、効率的な処理が可能になる。
デメリット
-
実装が複雑になる。Iteratorパターンを実装するには、Iteratorインターフェースを定義して、各コレクションの具象Iteratorクラスを実装する必要がある。また、コレクションやデータ構造を更新した際には、Iteratorも同時に更新する必要がある。
-
パフォーマンスの低下。Iteratorパターンを利用することで、要素にアクセスするためにインスタンスを生成する必要がある。Iteratorオブジェクトは集約オブジェクトと1対1の関係になるため、多数のIteratorオブジェクトを作成するとこれにより、パフォーマンスが低下する可能性がある。
MEMO
interface
メソッドの実装を定義せずに、 クラスが実装する必要があるメソッドを指定するコードを作成する。
https://www.php.net/manual/ja/language.oop5.interfaces.php
implements
interfaceを使用するときに使う演算子
https://www.php.net/manual/ja/language.oop5.interfaces.php
trait
いくつかの機能をまとめてコードを再利用するためのもの。
トレイトのメリットは、継承しなくても機能を共有できるところ。
https://www.php.net/manual/ja/language.oop5.traits.php