1. ritukiii

    Posted

    ritukiii
Changes in title
+phpで学ぶデザインパターン ~ 第1章 - Iterator ~
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,367 @@
+
+## 概要
+
+[java言語で学ぶデザインパターン入門](http://www.amazon.co.jp/%E5%A2%97%E8%A3%9C%E6%94%B9%E8%A8%82%E7%89%88Java%E8%A8%80%E8%AA%9E%E3%81%A7%E5%AD%A6%E3%81%B6%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3%E5%85%A5%E9%96%80-%E7%B5%90%E5%9F%8E-%E6%B5%A9/dp/4797327030/ref=sr_1_1?ie=UTF8&qid=1454418015&sr=8-1&keywords=java%E8%A8%80%E8%AA%9E%E3%81%A7%E5%AD%A6%E3%81%B6%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3%E5%85%A5%E9%96%80)を買って学んだ内容を、PHPに書き直して整理していく予定です。
+
+## Iteratorとは
+
+Iteratorパターンは、`数え上げの抽象化`などと表現されます。
+何が便利なのか、最初に普通に配列をforで回す例から、困る事を上げていって、Iteratorで解決してみます。
+
+## 1. 配列をforで回す
+
+この本では、本棚を表す`BookShlef`というクラスに、本を表現する`Book`クラスを追加してく例が紹介されてますので、それにそってやってきます。
+
+```php
+<?php
+
+class BookShelf
+{
+ private $books = [];
+ private $index = 0;
+
+ /**
+ * 本棚に本を追加
+ */
+ public function appendBook(Book $book)
+ {
+ $this->books[] = $book;
+ }
+
+ /**
+ * 指定した本を返す
+ */
+ public function getBookAt($index)
+ {
+ return $this->books[$index];
+ }
+
+ /**
+ * 本棚の大きさを返す
+ */
+ public function getLength()
+ {
+ return count($this->books);
+ }
+}
+
+class Book
+{
+ private $name = '';
+
+ public function __construct($name)
+ {
+ $this->name = $name;
+ }
+
+ public function getName()
+ {
+ return $this->name;
+ }
+}
+
+class Main
+{
+ public function exec()
+ {
+ $bookshelf = new BookShelf();
+ $bookshelf->appendBook(new Book('なぜ人は浮気を繰り返すのか'));
+ $bookshelf->appendBook(new Book('清楚系ビッチの生態'));
+ $bookshelf->appendBook(new Book('失敗する恋愛術'));
+
+ for ($i = 0; $i < $bookshelf->getLength(); $i++) {
+ echo $bookshelf->getBookAt($i)->getName().PHP_EOL;
+ }
+ }
+}
+
+$main = new Main();
+$main->exec();
+
+// => output
+// なぜ人は浮気を繰り返すのか
+// 清楚系ビッチの生態
+// 失敗する恋愛術
+```
+
+本棚(BookShelf)にBookを3冊追加し、順に取り出してます。
+まぁ良さそうですが、不便な点が2つあります。
+
+1. 今本棚には1次元配列でBookを格納してますが、これを2次元配列にした途端、このままでは動かなくなるので、`getBookAt`を修正する必要がある。
+2. 本を追加した順ではなく、最後に追加した順に表示したい場合、Mainクラスの実装を修正する必要がある
+
+## 2. BookShelfにIteratorを実装してみる
+
+```php
+<?php
+
+interface OriginalIterator
+{
+ public function hasNext();
+ public function next();
+}
+
+class BookShelf implements OriginalIterator
+{
+ private $books = [];
+ private $index = 0;
+
+ /**
+ * 本棚に本を追加
+ */
+ public function appendBook(Book $book)
+ {
+ $this->books[] = $book;
+ }
+
+ public function hasNext()
+ {
+ if ($this->index < $this->getLength()) {
+ return true;
+ }
+
+ return false;
+ }
+
+ public function next()
+ {
+ $book = $this->books[$this->index];
+ $this->index++;
+ return $book;
+ }
+
+ /**
+ * 本棚の大きさを返す
+ */
+ public function getLength()
+ {
+ return count($this->books);
+ }
+}
+
+class Book
+{
+ private $name = '';
+
+ public function __construct($name)
+ {
+ $this->name = $name;
+ }
+
+ public function getName()
+ {
+ return $this->name;
+ }
+}
+
+class Main
+{
+ public function exec()
+ {
+ $bookshelf = new BookShelf();
+ $bookshelf->appendBook(new Book('なぜ人は浮気を繰り返すのか'));
+ $bookshelf->appendBook(new Book('清楚系ビッチの生態'));
+ $bookshelf->appendBook(new Book('失敗する恋愛術'));
+
+ while ($bookshelf->hasNext()) {
+ echo $bookshelf->next()->getName().PHP_EOL;
+ }
+ }
+}
+
+$main = new Main();
+$main->exec();
+
+// => output
+// なぜ人は浮気を繰り返すのか
+// 清楚系ビッチの生態
+// 失敗する恋愛術
+```
+
+OriginalIteratorというインターフェイスを作り、BookShelfで実装してみました。
+これで、まぁ配列の形が変わってもMainをいじる事はなく、Iteratorをいじればなんとか出来そうです。
+
+ただ、最後に追加した順に走査したい場合、今度はその逆走用のBookShelfを作らないといけないため、この問題はまだ解決出来ないです。面倒です。
+
+じゃあ、最後に、いよいよ本で紹介されてたIteratorパターンを紹介します。
+
+## 3. 集約体から好きなイテレータを使えるようにする
+
+試しに、追加順に表示する順走のイテレータと、最後に追加した順から表示する逆走のイテレータを2つ作って使ってみます。
+
+```php
+<?php
+
+interface Aggregater
+{
+ public function orderIterator();
+ public function reverseIterator();
+}
+
+interface OriginalIterator
+{
+ public function hasNext();
+ public function next();
+}
+
+class BookShelf implements Aggregater
+{
+ private $books = [];
+
+ /**
+ * 本棚に本を追加
+ */
+ public function appendBook(Book $book)
+ {
+ $this->books[] = $book;
+ }
+
+ public function getBookAt($index)
+ {
+ return $this->books[$index];
+ }
+
+ /**
+ * 本棚の大きさを返す
+ */
+ public function getLength()
+ {
+ return count($this->books);
+ }
+
+ /**
+ * 順走イテレータ
+ */
+ public function orderIterator()
+ {
+ return new OrderIterator($this);
+ }
+
+ /**
+ * 順走イテレータ
+ */
+ public function reverseIterator()
+ {
+ return new ReverseIterator($this);
+ }
+}
+
+/**
+ * 順走イテレータ
+ */
+
+class OrderIterator implements OriginalIterator
+{
+ private $aggregate;
+ private $index = 0;
+
+ public function __construct($aggregate)
+ {
+ $this->aggregate = $aggregate;
+ }
+
+ public function hasNext()
+ {
+ if ($this->index < $this->aggregate->getLength()) {
+ return true;
+ }
+
+ return false;
+ }
+
+ public function next()
+ {
+ $book = $this->aggregate->getBookAt($this->index);
+ $this->index++;
+ return $book;
+ }
+}
+
+/**
+ * 逆走イテレータ
+ */
+class ReverseIterator implements OriginalIterator
+{
+ private $aggregate;
+ private $index = 0;
+
+ public function __construct($aggregate)
+ {
+ $this->aggregate = $aggregate;
+ $this->index = $this->aggregate->getLength() - 1;
+ }
+
+ public function hasNext()
+ {
+ return ($this->index >= 0);
+ }
+
+ public function next()
+ {
+ $book = $this->aggregate->getBookAt($this->index);
+ $this->index--;
+ return $book;
+ }
+}
+
+class Book
+{
+ private $name = '';
+
+ public function __construct($name)
+ {
+ $this->name = $name;
+ }
+
+ public function getName()
+ {
+ return $this->name;
+ }
+}
+
+class Main
+{
+ public function exec()
+ {
+ $bookshelf = new BookShelf();
+ $bookshelf->appendBook(new Book('なぜ人は浮気を繰り返すのか'));
+ $bookshelf->appendBook(new Book('清楚系ビッチの生態'));
+ $bookshelf->appendBook(new Book('失敗する恋愛術'));
+
+ $orderIterator = $bookshelf->orderIterator();
+
+ echo "順走イテレータ発動!!\n\n";
+ while ($orderIterator->hasNext()) {
+ echo $orderIterator->next()->getName().PHP_EOL;
+ }
+
+ $reverseIterator = $bookshelf->reverseIterator();
+
+ echo "\n\n逆走イテレータ発動!!\n\n";
+ while ($reverseIterator->hasNext()) {
+ echo $reverseIterator->next()->getName().PHP_EOL;
+ }
+ }
+}
+
+$main = new Main();
+$main->exec();
+
+// => output
+
+// 順走イテレータ発動!!
+
+// なぜ人は浮気を繰り返すのか
+// 清楚系ビッチの生態
+// 失敗する恋愛術
+
+
+// 逆走イテレータ発動!!
+
+// 失敗する恋愛術
+// 清楚系ビッチの生態
+// なぜ人は浮気を繰り返すのか
+```
+
+このように、集約体とは別でイテレータを実装し、それを使えるようにしておくことで、
+呼び出し元の実装を変える事なく、集約体(Aggregater)に対する走査を自由に行うように出来ます