mirror of
https://github.com/DesignPatternsPHP/DesignPatternsPHP.git
synced 2025-01-18 05:58:16 +01:00
Merge branch 'master' of github.com:domnikl/DesignPatternsPHP
This commit is contained in:
commit
ba14231163
@ -9,7 +9,7 @@ class BookList implements \Countable
|
||||
|
||||
public function getBook($bookNumberToGet)
|
||||
{
|
||||
if ((int)$bookNumberToGet <= $this->count()) {
|
||||
if (isset($this->books[$bookNumberToGet])) {
|
||||
return $this->books[$bookNumberToGet];
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@ class BookListIterator implements \Iterator
|
||||
/**
|
||||
* @var BookList
|
||||
*/
|
||||
protected $bookList;
|
||||
private $bookList;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
@ -61,7 +61,7 @@ class BookListIterator implements \Iterator
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
return $this->currentBook < $this->bookList->count();
|
||||
return null !== $this->bookList->getBook($this->currentBook);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2,22 +2,77 @@
|
||||
|
||||
namespace DesignPatterns\Behavioral\Iterator;
|
||||
|
||||
class BookListReverseIterator extends BookListIterator
|
||||
class BookListReverseIterator implements \Iterator
|
||||
{
|
||||
|
||||
/**
|
||||
* @var BookList
|
||||
*/
|
||||
private $bookList;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $currentBook = 0;
|
||||
|
||||
public function __construct(BookList $bookList)
|
||||
{
|
||||
$this->bookList = $bookList;
|
||||
$this->currentBook = $this->bookList->count() - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current book
|
||||
* @link http://php.net/manual/en/iterator.current.php
|
||||
* @return Book Can return any type.
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
return $this->bookList->getBook($this->currentBook);
|
||||
}
|
||||
|
||||
/**
|
||||
* (PHP 5 >= 5.0.0)<br/>
|
||||
* Move forward to next element
|
||||
* @link http://php.net/manual/en/iterator.next.php
|
||||
* @return void Any returned value is ignored.
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
$this->currentBook--;
|
||||
}
|
||||
|
||||
/**
|
||||
* (PHP 5 >= 5.0.0)<br/>
|
||||
* Return the key of the current element
|
||||
* @link http://php.net/manual/en/iterator.key.php
|
||||
* @return mixed scalar on success, or null on failure.
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
return $this->currentBook;
|
||||
}
|
||||
|
||||
/**
|
||||
* (PHP 5 >= 5.0.0)<br/>
|
||||
* Checks if current position is valid
|
||||
* @link http://php.net/manual/en/iterator.valid.php
|
||||
* @return boolean The return value will be casted to boolean and then evaluated.
|
||||
* Returns true on success or false on failure.
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
return 0 <= $this->currentBook;
|
||||
return null !== $this->bookList->getBook($this->currentBook);
|
||||
}
|
||||
|
||||
/**
|
||||
* (PHP 5 >= 5.0.0)<br/>
|
||||
* Rewind the Iterator to the first element
|
||||
* @link http://php.net/manual/en/iterator.rewind.php
|
||||
* @return void Any returned value is ignored.
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
$this->currentBook = $this->bookList->count() - 1;
|
||||
}
|
||||
}
|
||||
|
@ -4,12 +4,26 @@ namespace DesignPatterns\Behavioral\Memento;
|
||||
|
||||
class Caretaker
|
||||
{
|
||||
public static function run()
|
||||
protected $history = array();
|
||||
|
||||
/**
|
||||
* @return Memento
|
||||
*/
|
||||
public function getFromHistory($id)
|
||||
{
|
||||
/* @var $savedStates Memento[] */
|
||||
return $this->history[$id];
|
||||
}
|
||||
|
||||
$savedStates = array();
|
||||
/**
|
||||
* @param Memento $state
|
||||
*/
|
||||
public function saveToHistory(Memento $state)
|
||||
{
|
||||
$this->history[] = $state;
|
||||
}
|
||||
|
||||
public function runCustomLogic()
|
||||
{
|
||||
$originator = new Originator();
|
||||
|
||||
//Setting state to State1
|
||||
@ -17,17 +31,20 @@ class Caretaker
|
||||
//Setting state to State2
|
||||
$originator->setState("State2");
|
||||
//Saving State2 to Memento
|
||||
$savedStates[] = $originator->saveToMemento();
|
||||
$this->saveToHistory($originator->getStateAsMemento());
|
||||
//Setting state to State3
|
||||
$originator->setState("State3");
|
||||
|
||||
// We can request multiple mementos, and choose which one to roll back to.
|
||||
// Saving State3 to Memento
|
||||
$savedStates[] = $originator->saveToMemento();
|
||||
$this->saveToHistory($originator->getStateAsMemento());
|
||||
//Setting state to State4
|
||||
$originator->setState("State4");
|
||||
|
||||
$originator->restoreFromMemento($savedStates[1]);
|
||||
$originator->restoreFromMemento($this->getFromHistory(1));
|
||||
//State after restoring from Memento: State3
|
||||
|
||||
return $originator->getStateAsMemento()->getState();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -15,14 +15,17 @@ class Originator
|
||||
*/
|
||||
public function setState($state)
|
||||
{
|
||||
// you must check type of state inside child of this class
|
||||
// or use type-hinting for full pattern implementation
|
||||
$this->state = $state;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Memento
|
||||
*/
|
||||
public function saveToMemento()
|
||||
public function getStateAsMemento()
|
||||
{
|
||||
// you must save a separate copy in Memento
|
||||
$state = is_object($this->state) ? clone $this->state : $this->state;
|
||||
|
||||
return new Memento($state);
|
||||
|
@ -4,26 +4,43 @@
|
||||
Purpose
|
||||
-------
|
||||
|
||||
Provide the ability to restore an object to its previous state (undo via
|
||||
rollback).
|
||||
It provides the ability to restore an object to it's previous state (undo
|
||||
via rollback) or to gain access to state of the object, without revealing
|
||||
it's implementation (i.e., the object is not required to have a functional
|
||||
for return the current state).
|
||||
|
||||
The memento pattern is implemented with three objects: the originator, a
|
||||
caretaker and a memento. The originator is some object that has an
|
||||
internal state. The caretaker is going to do something to the
|
||||
originator, but wants to be able to undo the change. The caretaker first
|
||||
asks the originator for a memento object. Then it does whatever
|
||||
operation (or sequence of operations) it was going to do. To roll back
|
||||
to the state before the operations, it returns the memento object to the
|
||||
originator. The memento object itself is an opaque object (one which the
|
||||
caretaker cannot, or should not, change). When using this pattern, care
|
||||
should be taken if the originator may change other objects or resources
|
||||
- the memento pattern operates on a single object.
|
||||
The memento pattern is implemented with three objects: the Originator, a
|
||||
Caretaker and a Memento.
|
||||
|
||||
Memento – an object that *contains a concrete unique snapshot of state* of
|
||||
any object or resource: string, number, array, an instance of class and so on.
|
||||
The uniqueness in this case does not imply the prohibition existence of similar
|
||||
states in different snapshots. That means the state can be extracted as
|
||||
the independent clone. Any object stored in the Memento should be
|
||||
*a full copy of the original object rather than a reference* to the original
|
||||
object. The Memento object is a "opaque object" (the object that no one can
|
||||
or should change).
|
||||
|
||||
Originator – it is an object that contains the *actual state of an external
|
||||
object is strictly specified type*. Originator is able to create a unique
|
||||
copy of this state and return it wrapped in a Memento. The Originator does
|
||||
not know the history of changes. You can set a concrete state to Originator
|
||||
from the outside, which will be considered as actual. The Originator must
|
||||
make sure that given state corresponds the allowed type of object. Originator
|
||||
may (but not should) have any methods, but they *they can't make changes to
|
||||
the saved object state*.
|
||||
|
||||
Caretaker *controls the states history*. He may make changes to an object;
|
||||
take a decision to save the state of an external object in the Originator;
|
||||
ask from the Originator snapshot of the current state; or set the Originator
|
||||
state to equivalence with some snapshot from history.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
- The seed of a pseudorandom number generator
|
||||
- The state in a finite state machine
|
||||
- Control for intermediate states of `ORM Model <http://en.wikipedia.org/wiki/Object-relational_mapping>`_ before saving
|
||||
|
||||
UML Diagram
|
||||
-----------
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
namespace DesignPatterns\Behavioral\Memento\Tests;
|
||||
|
||||
use DesignPatterns\Behavioral\Memento\Caretaker;
|
||||
use DesignPatterns\Behavioral\Memento\Memento;
|
||||
use DesignPatterns\Behavioral\Memento\Originator;
|
||||
|
||||
/**
|
||||
@ -10,6 +12,37 @@ use DesignPatterns\Behavioral\Memento\Originator;
|
||||
class MementoTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
|
||||
public function testUsageExample()
|
||||
{
|
||||
$originator = new Originator();
|
||||
$caretaker = new Caretaker();
|
||||
|
||||
$character = new \stdClass();
|
||||
$character->name = "Gandalf"; // new object
|
||||
$originator->setState($character); // connect Originator to character object
|
||||
|
||||
$character->name = "Gandalf the Grey"; // work on the object
|
||||
$character->race = "Maia"; // still change something
|
||||
$snapshot = $originator->getStateAsMemento(); // time to save state
|
||||
$caretaker->saveToHistory($snapshot); // put state to log
|
||||
|
||||
$character->name = "Sauron"; // change something
|
||||
$character->race = "Ainur"; // and again
|
||||
$this->assertAttributeEquals($character, "state", $originator); // state inside the Originator was equally changed
|
||||
|
||||
$snapshot = $originator->getStateAsMemento(); // time to save another state
|
||||
$caretaker->saveToHistory($snapshot); // put state to log
|
||||
|
||||
$rollback = $caretaker->getFromHistory(0);
|
||||
$originator->restoreFromMemento($rollback); // return to first state
|
||||
$character = $rollback->getState(); // use character from old state
|
||||
|
||||
$this->assertEquals("Gandalf the Grey", $character->name); // yes, that what we need
|
||||
$character->name = "Gandalf the White"; // make new changes
|
||||
|
||||
$this->assertAttributeEquals($character, "state", $originator); // and Originator linked to actual object again
|
||||
}
|
||||
|
||||
public function testStringState()
|
||||
{
|
||||
$originator = new Originator();
|
||||
@ -18,52 +51,94 @@ class MementoTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertAttributeEquals("State1", "state", $originator);
|
||||
|
||||
$originator->setState("State2");
|
||||
|
||||
$this->assertAttributeEquals("State2", "state", $originator);
|
||||
|
||||
$savedState = $originator->saveToMemento();
|
||||
|
||||
$this->assertAttributeEquals("State2", "state", $savedState);
|
||||
$snapshot = $originator->getStateAsMemento();
|
||||
$this->assertAttributeEquals("State2", "state", $snapshot);
|
||||
|
||||
$originator->setState("State3");
|
||||
|
||||
$this->assertAttributeEquals("State3", "state", $originator);
|
||||
|
||||
$originator->restoreFromMemento($savedState);
|
||||
|
||||
$originator->restoreFromMemento($snapshot);
|
||||
$this->assertAttributeEquals("State2", "state", $originator);
|
||||
}
|
||||
|
||||
public function testObjectState()
|
||||
public function testSnapshotIsClone()
|
||||
{
|
||||
$originator = new Originator();
|
||||
$object = new \stdClass();
|
||||
|
||||
$originator->setState($object);
|
||||
$snapshot = $originator->getStateAsMemento();
|
||||
$object->new_property = 1;
|
||||
|
||||
$this->assertAttributeEquals($object, "state", $originator);
|
||||
$this->assertAttributeNotEquals($object, "state", $snapshot);
|
||||
|
||||
$originator->restoreFromMemento($snapshot);
|
||||
$this->assertAttributeNotEquals($object, "state", $originator);
|
||||
}
|
||||
|
||||
public function testCanChangeActualState()
|
||||
{
|
||||
$originator = new Originator();
|
||||
$first_state = new \stdClass();
|
||||
|
||||
$originator->setState($first_state);
|
||||
$snapshot = $originator->getStateAsMemento();
|
||||
$second_state = $snapshot->getState();
|
||||
|
||||
$first_state->first_property = 1; // still actual
|
||||
$second_state->second_property = 2; // just history
|
||||
$this->assertAttributeEquals($first_state, "state", $originator);
|
||||
$this->assertAttributeNotEquals($second_state, "state", $originator);
|
||||
|
||||
$originator->restoreFromMemento($snapshot);
|
||||
$first_state->first_property = 11; // now it lost state
|
||||
$second_state->second_property = 22; // must be actual
|
||||
$this->assertAttributeEquals($second_state, "state", $originator);
|
||||
$this->assertAttributeNotEquals($first_state, "state", $originator);
|
||||
}
|
||||
|
||||
public function testStateWithDifferentObjects()
|
||||
{
|
||||
$originator = new Originator();
|
||||
|
||||
$foo = new \stdClass();
|
||||
$foo->data = "foo";
|
||||
$first = new \stdClass();
|
||||
$first->data = "foo";
|
||||
|
||||
$originator->setState($foo);
|
||||
$originator->setState($first);
|
||||
$this->assertAttributeEquals($first, "state", $originator);
|
||||
|
||||
$this->assertAttributeEquals($foo, "state", $originator);
|
||||
$first_snapshot = $originator->getStateAsMemento();
|
||||
$this->assertAttributeEquals($first, "state", $first_snapshot);
|
||||
|
||||
$savedState = $originator->saveToMemento();
|
||||
$second = new \stdClass();
|
||||
$second->data = "bar";
|
||||
$originator->setState($second);
|
||||
$this->assertAttributeEquals($second, "state", $originator);
|
||||
|
||||
$this->assertAttributeEquals($foo, "state", $savedState);
|
||||
$originator->restoreFromMemento($first_snapshot);
|
||||
$this->assertAttributeEquals($first, "state", $originator);
|
||||
}
|
||||
|
||||
$bar = new \stdClass();
|
||||
$bar->data = "bar";
|
||||
public function testCaretaker()
|
||||
{
|
||||
$caretaker = new Caretaker();
|
||||
$memento1 = new Memento("foo");
|
||||
$memento2 = new Memento("bar");
|
||||
$caretaker->saveToHistory($memento1);
|
||||
$caretaker->saveToHistory($memento2);
|
||||
$this->assertAttributeEquals(array($memento1, $memento2), "history", $caretaker);
|
||||
$this->assertEquals($memento1, $caretaker->getFromHistory(0));
|
||||
$this->assertEquals($memento2, $caretaker->getFromHistory(1));
|
||||
|
||||
$originator->setState($bar);
|
||||
}
|
||||
|
||||
$this->assertAttributeEquals($bar, "state", $originator);
|
||||
|
||||
$originator->restoreFromMemento($savedState);
|
||||
|
||||
$this->assertAttributeEquals($foo, "state", $originator);
|
||||
|
||||
$foo->data = null;
|
||||
|
||||
$this->assertAttributeNotEquals($foo, "state", $savedState);
|
||||
|
||||
$this->assertAttributeNotEquals($foo, "state", $originator);
|
||||
public function testCaretakerCustomLogic()
|
||||
{
|
||||
$caretaker = new Caretaker();
|
||||
$result = $caretaker->runCustomLogic();
|
||||
$this->assertEquals("State3", $result);
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 61 KiB |
@ -1,29 +1,32 @@
|
||||
#
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: DesignPatternsPHP 1.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-05-29 12:18+0200\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"PO-Revision-Date: 2015-05-30 01:42+0300\n"
|
||||
"Last-Translator: Eugene Glotov <kivagant@gmail.com>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ru\n"
|
||||
|
||||
#: ../../Behavioral/Memento/README.rst:2
|
||||
msgid "`Memento`__"
|
||||
msgstr ""
|
||||
msgstr "`Хранитель`__"
|
||||
|
||||
#: ../../Behavioral/Memento/README.rst:5
|
||||
msgid "Purpose"
|
||||
msgstr ""
|
||||
msgstr "Назначение"
|
||||
|
||||
#: ../../Behavioral/Memento/README.rst:7
|
||||
msgid ""
|
||||
"Provide the ability to restore an object to its previous state (undo via "
|
||||
"rollback)."
|
||||
msgstr ""
|
||||
"Предоставляет возможность восстановить объект в его предыдущем состоянии или "
|
||||
"получить доступ к состоянию объекта, не раскрывая его реализацию (т.е. сам "
|
||||
"объект не обязан иметь функционал возврата текущего состояния)."
|
||||
|
||||
#: ../../Behavioral/Memento/README.rst:10
|
||||
msgid ""
|
||||
@ -39,47 +42,74 @@ msgid ""
|
||||
"other objects or resources - the memento pattern operates on a single "
|
||||
"object."
|
||||
msgstr ""
|
||||
"Паттерн «Хранитель» реализуется тремя объектами: Создатель, Опекун и "
|
||||
"Хранитель.\n"
|
||||
"\n"
|
||||
"Хранитель — объект, который *хранит конкретный уникальный слепок состояния* "
|
||||
"любого объекта или ресурса: строка, число, массив, экземпляр класса и так "
|
||||
"далее. Уникальность в данном случае подразумевает не запрет существования "
|
||||
"одинаковых состояний в слепках, а то, что состояние можно извлечь в виде "
|
||||
"независимого клона. Это значит, объект, сохраняемый в Хранитель, должен *быть "
|
||||
"полной копией исходного объекта а не ссылкой* на исходный объект. Сам объект "
|
||||
"Хранитель является «непрозрачным объектом» (тот, который никто не может и не "
|
||||
"должен изменять).\n"
|
||||
"\n"
|
||||
"Создатель — это объект, который *содержит в себе актуальное состояние внешнего "
|
||||
"объекта строго заданного типа* и умеет создать уникальную копию этого "
|
||||
"состояния, возвращая её обёрнутую в Хранитель. Создатель не знает истории "
|
||||
"изменений. Создателю можно принудительно установить конкретное состояние "
|
||||
"извне, которое будет считаться актуальным. Создатель должен позаботиться, "
|
||||
"чтобы это состояние соответствовало типу объекта, с которым ему разрешено "
|
||||
"работать. Создатель может (но не обязан) иметь любые методы, но они *не могут "
|
||||
"менять сохранённое состояние объекта*.\n"
|
||||
"\n"
|
||||
"Опекун *управляет историей слепков состояний*. Он может вносить изменения в "
|
||||
"объект, принимать решение о сохранении состояния внешнего объекта в Создателе, "
|
||||
"требовать от Создателя слепок текущего состояния, или привести состояние "
|
||||
"Создателя в соответствие состоянию какого-то слепка из истории."
|
||||
|
||||
#: ../../Behavioral/Memento/README.rst:23
|
||||
msgid "Examples"
|
||||
msgstr ""
|
||||
msgstr "Примеры"
|
||||
|
||||
#: ../../Behavioral/Memento/README.rst:25
|
||||
msgid "The seed of a pseudorandom number generator"
|
||||
msgstr ""
|
||||
"`Семя <http://en.wikipedia.org/wiki/Random_seed>`_ псевдослучайного генератора "
|
||||
"чисел."
|
||||
|
||||
#: ../../Behavioral/Memento/README.rst:26
|
||||
msgid "The state in a finite state machine"
|
||||
msgstr ""
|
||||
msgstr "Состояние конечного автомата"
|
||||
|
||||
#: ../../Behavioral/Memento/README.rst:29
|
||||
msgid "UML Diagram"
|
||||
msgstr ""
|
||||
msgstr "UML Диаграмма"
|
||||
|
||||
#: ../../Behavioral/Memento/README.rst:36
|
||||
msgid "Code"
|
||||
msgstr ""
|
||||
msgstr "Код"
|
||||
|
||||
#: ../../Behavioral/Memento/README.rst:38
|
||||
msgid "You can also find these code on `GitHub`_"
|
||||
msgstr ""
|
||||
msgstr "Вы можете найти этот код на `GitHub`_"
|
||||
|
||||
#: ../../Behavioral/Memento/README.rst:40
|
||||
msgid "Memento.php"
|
||||
msgstr ""
|
||||
msgstr "Memento.php"
|
||||
|
||||
#: ../../Behavioral/Memento/README.rst:46
|
||||
msgid "Originator.php"
|
||||
msgstr ""
|
||||
msgstr "Originator.php"
|
||||
|
||||
#: ../../Behavioral/Memento/README.rst:52
|
||||
msgid "Caretaker.php"
|
||||
msgstr ""
|
||||
msgstr "Caretaker.php"
|
||||
|
||||
#: ../../Behavioral/Memento/README.rst:59
|
||||
msgid "Test"
|
||||
msgstr ""
|
||||
msgstr "Тест"
|
||||
|
||||
#: ../../Behavioral/Memento/README.rst:61
|
||||
msgid "Tests/MementoTest.php"
|
||||
msgstr ""
|
||||
msgstr "Tests/MementoTest.php"
|
||||
|
Loading…
x
Reference in New Issue
Block a user