cheka.jp 超不定期で更新する写真達。お口直しにどうぞ。

[写経] 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さん

構造に対して手を加えずに別パターンの抽出方法を適用することが出来ました。
日付けを追加して未来 = 未発売で抽出するのも分かりやすいサンプルだと思いますので、是非チャレンジしてみて下さい。

Add a Comment

メールアドレスが公開されることはありません。