mirror of
https://github.com/DesignPatternsPHP/DesignPatternsPHP.git
synced 2025-05-22 06:21:46 +02:00
Merge branch 'iterator-a-simpler-example'
This commit is contained in:
commit
986aa7cf02
80
Iterator/CardGame.php
Normal file
80
Iterator/CardGame.php
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DesignPatternPHP
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace DesignPatterns\Iterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterator provides a standard way to iterate over a collection without knowing
|
||||||
|
* how it is implemented. All you need to know is : you can traverse it
|
||||||
|
* with current, valid, next, rewind and key.
|
||||||
|
*
|
||||||
|
* That's the key feature of this pattern :
|
||||||
|
* The underlaying machinery could be an array, a matrix, a file, a cursor
|
||||||
|
* from database, a webservice with a cache, you don't care anymore.
|
||||||
|
*
|
||||||
|
* Note: This design pattern changes from one language to another. It depends
|
||||||
|
* mostly how loop statements handle collections (see Java before and after 1.5)
|
||||||
|
*
|
||||||
|
* In this simple example, I try to demonstrate how I manage a "linear" iterator
|
||||||
|
* on a card game but in fact, the underlaying storage is handled by two combined
|
||||||
|
* arrays.
|
||||||
|
*
|
||||||
|
* If tomorrow you decide to read cards from a database, the client
|
||||||
|
* (see the PHPUnit test) will remain unchanged. That's beauty of it.
|
||||||
|
*/
|
||||||
|
class CardGame implements \Iterator
|
||||||
|
{
|
||||||
|
|
||||||
|
protected $color = array('D', 'S', 'C', 'H');
|
||||||
|
protected $number = array(7, 8, 9, 10, 'J', 'Q', 'K', 'A');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the current value
|
||||||
|
*/
|
||||||
|
public function current()
|
||||||
|
{
|
||||||
|
return current($this->number) . ' of ' . current($this->color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the current key
|
||||||
|
*/
|
||||||
|
public function key()
|
||||||
|
{
|
||||||
|
return current($this->color) . current($this->number);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Go to the next item in the collection
|
||||||
|
*/
|
||||||
|
public function next()
|
||||||
|
{
|
||||||
|
if (false === next($this->number)) {
|
||||||
|
if (false !== next($this->color)) {
|
||||||
|
reset($this->number);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Go to the first item in the collection
|
||||||
|
*/
|
||||||
|
public function rewind()
|
||||||
|
{
|
||||||
|
reset($this->color);
|
||||||
|
reset($this->number);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the current position a valid item (true)
|
||||||
|
* or do we reach the end (false) ?
|
||||||
|
*/
|
||||||
|
public function valid()
|
||||||
|
{
|
||||||
|
return current($this->number) || current($this->color);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -32,6 +32,8 @@ class File
|
|||||||
|
|
||||||
public function process()
|
public function process()
|
||||||
{
|
{
|
||||||
|
// this is the place to show how using an iterator, with foreach
|
||||||
|
// See the CardGame.php file
|
||||||
$this->_rowset->process();
|
$this->_rowset->process();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -55,6 +57,14 @@ class Rowset implements Iterator
|
|||||||
public function process()
|
public function process()
|
||||||
{
|
{
|
||||||
// this actually calls rewind(), { next(), valid(), key() and current() :}
|
// this actually calls rewind(), { next(), valid(), key() and current() :}
|
||||||
|
/**
|
||||||
|
* THE key feature of the Iterator Pattern is to provide a *public contract*
|
||||||
|
* to iterate on a collection without knowing how items are handled inside
|
||||||
|
* the collection. It is not just an easy way to use "foreach"
|
||||||
|
*
|
||||||
|
* One cannot see the point of iterator pattern if you iterate on $this.
|
||||||
|
* This example is unclear and mixed with some Composite pattern ideas.
|
||||||
|
*/
|
||||||
foreach ($this as $line => $row) {
|
foreach ($this as $line => $row) {
|
||||||
$row->process();
|
$row->process();
|
||||||
}
|
}
|
||||||
|
56
Tests/Iterator/IteratorTest.php
Normal file
56
Tests/Iterator/IteratorTest.php
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DesignPatternPHP
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace DesignPatterns\Tests\Iterator;
|
||||||
|
|
||||||
|
use DesignPatterns\Iterator\CardGame;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IteratorTest the CardGame iterator
|
||||||
|
*
|
||||||
|
* @author flo
|
||||||
|
*/
|
||||||
|
class IteratorTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
|
||||||
|
protected $game;
|
||||||
|
|
||||||
|
protected function setUp()
|
||||||
|
{
|
||||||
|
$this->game = new CardGame();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the client of the iterator.
|
||||||
|
* It remains unchanged even if one I decide to use MongoDB to store the
|
||||||
|
* card.
|
||||||
|
*/
|
||||||
|
public function testCardinal()
|
||||||
|
{
|
||||||
|
$counter = 0;
|
||||||
|
foreach ($this->game as $key => $card) {
|
||||||
|
$counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertEquals(32, $counter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Some fancy functions of PHP.
|
||||||
|
*/
|
||||||
|
public function testExampleOf_PHP_Helper()
|
||||||
|
{
|
||||||
|
// PHPUnit works on array or iterator :
|
||||||
|
$this->assertCount(32, $this->game);
|
||||||
|
// a easy way to get an array from interator :
|
||||||
|
$cards = iterator_to_array($this->game);
|
||||||
|
$this->assertEquals('A of S', $cards['SA']);
|
||||||
|
// a easy way to get an iterator from an array :
|
||||||
|
$iterator = new \ArrayIterator($cards);
|
||||||
|
$this->assertInstanceOf('\Iterator', $iterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user