[写経] PHPで学ぶデザインパターン06
|— 注意 —
PHPによるデザインパターンの写経ですが、まんま書くと問題があるのでアウトプットとして備忘録メモです
初心者には分かりやすくて良い本なので、是非本屋でチェックして下さい。
URL: http://www.amazon.co.jp/PHPによるデザインパターン入門-下岡-秀幸/dp/4798015164
PHPによるデザインパターン入門
Iteratorで集約オブジェクトにアクセスする
振る舞い + オブジェクト
中身が分かって単純な配列であればforやforeachを使ってリストにアクセスすることが出来ます。
集約されたオブジェクトに対しても再帰処理やロジックをloop内に構築してデータへアクセスすることが可能ですが、利用側が集約オブジェクトの中身を熟知している必要があります。
Iteratorパターンは色々なオブジェクトが集約された集約クラスに対して「ユーザーが欲しい情報」を「中身を知らなくても」反復的に取り出すためのAPIを提供するパターンです。
[写経] PHPで学ぶデザインパターン05で準備した本オブジェクトに対してイテレータを行うインターフェイスを準備してみましょう。
(PHP5.3からSPL拡張が必須になるのでSPL拡張を利用して実装を進めます)
前回利用したクラスを準備して少し手を加えます
/* 貸し出される本クラス */ class Book { //本のID private $id; //本の名前 private $title; //貸し出しフラグ private $rental; //コンストラクタ public function __construct($id,$title) { $this->id = $id; $this->title = $title; $this->rental = false; } //ID取得 public function getId() { return $this->id; } //名称取得 public function getTitle() { return $this->title; } //貸し出しフラグ取得 public function getRental() { return $this->rental; } //貸し出しフラグの設定 public function setRental($rental) { $this->rental = $rental; } }
/* 本の一覧を管理するDaoクラス */ class BookDao { //シングルトン用のインスタンス private static $instance; //本の一覧 private $books; //コンストラクタ private function __construct() { $this->books = array(); for ($i=1;$i<=10;$i++) { $book = new Book($i,"本タイトル_{$i}"); //何冊かは貸出中にする if( !($i%3) ) $book->setRental(true); $this->books[$book->getId()] = $book; } } //複製を禁止する(継承されてもエラーにするようにfinalで宣言する) public final function __clone() { throw new RuntimeException ("複製したらダメだよ!!"); } //シングルトンの取得 public static function getInstance() { if(!isset(self::$instance)){ self::$instance = new self(); } return self::$instance; } //本の追加 public function add(Book $book) { $id = $book->getId(); $new = (empty($this->books[$id])) ? true : false; $this->books[$id] = $book; return $new; } //検索 public function findById($id,$renting=true) { if(array_key_exists($id,$this->books)){ if($renting) { //レンタル中の本は省く return ($this->books[$id]->getRental()) ? null : $this->books[$id]; }else{ return $this->books[$id]; } }else{ return null; } } //全て検索 public function findAll() { return $this->books; } //貸し出し public function setRental($id){ $book = $this->findById($id); if(!empty($book)){ $this->books[$book->getId()]->setRental(true); } } }
イテレータ機能を提供するクラス
/* 本に対してイテレータ機能を提供する SPL拡張のAggregateインターフェイスを実装する */ class BookAggregate implements IteratorAggregate{ //本の一覧を保存 private $bookList; //Daoクラス private $bookDao; //コンストラクタ public function __construct() { $this->bookDao = BookDao::getInstance(); $this->bookList = new ArrayObject(); //初期値の追加 foreach ($this->bookDao->findAll() as $book) { $this->bookList[] = $book; } } //リストの追加 public function add(Book $book){ $this->bookDao->add($book); $this->bookList[] = $book; } //イテレータの取得 public function getIterator(){ return $this->bookList->getIterator(); } }
単純なパターンを試してみる
/* 利用してみる */ //リストに本を追加してみる $bookAggregate = new BookAggregate(); $bookAggregate->add(new Book(20,"来月発売 Macさん")); $bookAggregate->add(new Book(30,"来月発売 iPhoneさん")); //イテレータを使って取り出してみる $iterator = $bookAggregate->getIterator(); foreach ($iterator as $book) { print "ID :".$book->getId().",TITLE : ".$book->getTitle()."\n"; }
結果確認
$php iterator.php ID :1,TITLE : 本タイトル_1 ID :2,TITLE : 本タイトル_2 ID :3,TITLE : 本タイトル_3 ID :4,TITLE : 本タイトル_4 ID :5,TITLE : 本タイトル_5 ID :6,TITLE : 本タイトル_6 ID :7,TITLE : 本タイトル_7 ID :8,TITLE : 本タイトル_8 ID :9,TITLE : 本タイトル_9 ID :10,TITLE : 本タイトル_10 ID :20,TITLE : 来月発売 Macさん ID :30,TITLE : 来月発売 iPhoneさん
リストをフィルタリングする
遅くなりましたが、今日のメインはここからです。
リストに対してIDが20以上の本だけ抽出するイテレータを定義してみましょう。
/* ID20以上のリストを抽出するイテレータ */ class BookAggregateOver20 extends FilterIterator{ //コンストラクタ public function __construct($iterator) { parent::__construct($iterator); } //フィルタリング public function accept() { $book = $this->current(); return (20<=$book->getId()); } }
//リストに本を追加してみる $bookAggregate = new BookAggregate(); $bookAggregate->add(new Book(20,"来月発売 Macさん")); $bookAggregate->add(new Book(30,"来月発売 iPhoneさん")); //新しいイテレータを使って取り出してみる $iterator = new BookAggregateOver20($bookAggregate->getIterator()); foreach ($iterator as $book) { print "ID :".$book->getId().",TITLE : ".$book->getTitle()."\n"; }
結果確認
$php iterator.php ID :20,TITLE : 来月発売 Macさん ID :30,TITLE : 来月発売 iPhoneさん
構造に対して手を加えずに別パターンの抽出方法を適用することが出来ました。
日付けを追加して未来 = 未発売で抽出するのも分かりやすいサンプルだと思いますので、是非チャレンジしてみて下さい。