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

[写経] PHPで学ぶデザインパターン05

— 注意 —
PHPによるデザインパターンの写経ですが、まんま書くと問題があるのでアウトプットとして備忘録メモです

初心者には分かりやすくて良い本なので、是非本屋でチェックして下さい。

URL: http://www.amazon.co.jp/PHPによるデザインパターン入門-下岡-秀幸/dp/4798015164
PHPによるデザインパターン入門

FacadeでAPIをシンプルにする

構造 + クラス、オブジェクト

複雑な関連をもつクラス郡に対してシンプルなAPIを準備するパターンです。
ドメイン駆動設計でも出てくるパターンなので使う事が多いかもしれません。

まずは複雑な関連をもつクラス群を作成

図書館にある本を貸し出すサブシステムを構築します。
見やすくするために改行を入れていますが、適度に修正して下さい。
また、複雑な関連を表現するため実ロジックは簡易的なものにしています。

本を表現するクラスです。

/*
貸し出される本クラス
*/
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;
	}
}

貸し出す本を管理するクラスです。

/*
貸し出を行う本を管理するクラス
*/
class RentalBook {
	//管理する本
	private $book;
	//コンストラクタ
	public function __construct(Book $book) {
		$this->book = $book;
	}
	//ゲッター
	public function getBook() {
		return $this->book;
	}
}

貸し出を管理するクラスです。

/*
貸し出を管理するクラス
*/
class Rental {
	//貸し出す本
	private $rentalBook;
	//コンストラクタ
	public function __construct() {
		$this->rentalBook = array();
	}
	//貸し出す本の追加
	public function addRentalBook(RentalBook $rentalBook) {
		$book = $rentalBook->getBook();
		$this->rentalBook[$book->getId()] = $rentalBook;
	}
	//ゲッター
	public function getRentalBook() {
		return $this->rentalBook;
	}
}

本の一覧を管理するDaoクラスです。
本来はファイルやDBに対するAPIを定義、実装するクラスです。

/*
本の一覧を管理する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 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);
		}
	}
}

貸し出しを管理するDaoクラスです。
本来はレンタル中の情報をDBなどに保存する責務を負います。

/*
貸し出しを管理するDaoクラス
*/
class RentalDao{
	//貸し出しを行う
	public static function Rental(Rental $rental) {
		//BookDaoを準備
		$bookDao = BookDao::getInstance();
		//貸し出しを行う
		print "以下の本をレンタルしました。\n";
		foreach ($rental->getRentalBook() as $rentalBook) {
			$book = $rentalBook->getBook();
			$bookDao->setRental($book->getId());
			print "タイトル : ".$book->getTitle()."\n";
		}
		print "\n";
	}
}

Facadeクラスを定義してAPIを簡易的にしよう

このままでも準備したクラス群を使って自由にデータのやりとりを行うことは可能ですが、様々なクライアントで自由にクラスを利用されている場合、データ仕様変更や新しいクラスの追加などの影響範囲が分かりにくく複雑なシステムになってしまいます。
クライアントの利用を制限してクラス群との関連を緩くするためにFacadeパターンを利用してみましょう。

/*
ファサードクラス
*/
class Fasade{
	//貸し出し予約
	private static $Rental;
	//本の追加
	public static function ReserveBook($id) {
		//レンタルの初期化
		if(!isset(self::$Rental)) self::$Rental = new Rental();
		//Daoの準備
		$bookDao = BookDao::getInstance();
		//対象の本を取得
		$book = $bookDao->findById($id,false);
		//レンタル中でない場合はレンタル予約を行う
		if(!empty($book)){
			if($book->getRental()){
				print $book->getTitle()."は貸出中です。\n";
			}else{
				self::$Rental->addRentalBook(new RentalBook($book));
			}
		}else{
			print "お探しの本が見つかりません\n";
		}
	}
	//予約の実行
	public static function RentalBook() {
		//レンタルの初期化
		print "\n";
		if(!isset(self::$Rental)) self::$Rental = new Rental();
		RentalDao::Rental(self::$Rental);
	}
	//予約のキャンセル
	public static function Cancel() {
		self::$Rental = new Rental();
	}
	//全ての本の一覧を表示
	public static function showList() {
		//Daoの準備
		$bookList = BookDao::getInstance()->findAll();
		print "図書館の本一覧です\n";
		foreach ($bookList as $book) {
			print $book->getTitle()." : ".( ($book->getRental()) ? '貸出中' : 'レンタルできます' )."\n";
		}
		print "\n";
	}
}

さぁ、使ってみましょう

FasadeでシンプルなAPIを定義したのでクライアントのコードがシンプルになったのが分かると思います。

/*
利用してみる
*/

//現在のレンタル状況を確認する
Fasade::showList();

//本をレンタル予約する
Fasade::ReserveBook(1);
Fasade::ReserveBook(2);
//貸出中の本を予約する
Fasade::ReserveBook(3);
//存在しない本を予約する
Fasade::ReserveBook(100);

//レンタルする
Fasade::RentalBook();

//レンタル後の状況を確認する
Fasade::showList();

結果確認

$php fasade.php
図書館の本一覧です
本タイトル_1 : レンタルできます
本タイトル_2 : レンタルできます
本タイトル_3 : 貸出中
本タイトル_4 : レンタルできます
本タイトル_5 : レンタルできます
本タイトル_6 : 貸出中
本タイトル_7 : レンタルできます
本タイトル_8 : レンタルできます
本タイトル_9 : 貸出中
本タイトル_10 : レンタルできます

本タイトル_3は貸出中です。
お探しの本が見つかりません

以下の本をレンタルしました。
タイトル : 本タイトル_1
タイトル : 本タイトル_2

図書館の本一覧です
本タイトル_1 : 貸出中
本タイトル_2 : 貸出中
本タイトル_3 : 貸出中
本タイトル_4 : レンタルできます
本タイトル_5 : レンタルできます
本タイトル_6 : 貸出中
本タイトル_7 : レンタルできます
本タイトル_8 : レンタルできます
本タイトル_9 : 貸出中
本タイトル_10 : レンタルできます

Add a Comment

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