diff --git a/Behavioral/Memento/Caretaker.php b/Behavioral/Memento/Caretaker.php deleted file mode 100644 index d80454a..0000000 --- a/Behavioral/Memento/Caretaker.php +++ /dev/null @@ -1,49 +0,0 @@ -history[$id]; - } - - /** - * @param Memento $state - */ - public function saveToHistory(Memento $state) - { - $this->history[] = $state; - } - - public function runCustomLogic() - { - $originator = new Originator(); - - //Setting state to State1 - $originator->setState('State1'); - //Setting state to State2 - $originator->setState('State2'); - //Saving State2 to Memento - $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 - $this->saveToHistory($originator->getStateAsMemento()); - //Setting state to State4 - $originator->setState('State4'); - - $originator->restoreFromMemento($this->getFromHistory(1)); - //State after restoring from Memento: State3 - - return $originator->getStateAsMemento()->getState(); - } -} diff --git a/Behavioral/Memento/Memento.php b/Behavioral/Memento/Memento.php index 4dd2fc8..f75fcc9 100644 --- a/Behavioral/Memento/Memento.php +++ b/Behavioral/Memento/Memento.php @@ -4,19 +4,21 @@ namespace DesignPatterns\Behavioral\Memento; class Memento { - /* @var mixed */ + /** + * @var State + */ private $state; /** - * @param mixed $stateToSave + * @param State $stateToSave */ - public function __construct($stateToSave) + public function __construct(State $stateToSave) { $this->state = $stateToSave; } /** - * @return mixed + * @return State */ public function getState() { diff --git a/Behavioral/Memento/Originator.php b/Behavioral/Memento/Originator.php deleted file mode 100644 index 3acb0c1..0000000 --- a/Behavioral/Memento/Originator.php +++ /dev/null @@ -1,38 +0,0 @@ -state = $state; - } - - /** - * @return Memento - */ - 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); - } - - public function restoreFromMemento(Memento $memento) - { - $this->state = $memento->getState(); - } -} diff --git a/Behavioral/Memento/README.rst b/Behavioral/Memento/README.rst index 911e30e..32ec703 100644 --- a/Behavioral/Memento/README.rst +++ b/Behavioral/Memento/README.rst @@ -6,8 +6,8 @@ Purpose 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). +it's implementation (i.e., the object is not required to have a function +to return the current state). The memento pattern is implemented with three objects: the Originator, a Caretaker and a Memento. @@ -66,9 +66,9 @@ Originator.php :language: php :linenos: -Caretaker.php +Ticket.php -.. literalinclude:: Caretaker.php +.. literalinclude:: Ticket.php :language: php :linenos: diff --git a/Behavioral/Memento/State.php b/Behavioral/Memento/State.php new file mode 100644 index 0000000..6cb5dd1 --- /dev/null +++ b/Behavioral/Memento/State.php @@ -0,0 +1,48 @@ +state = $state; + } + + private static function ensureIsValidState(string $state) + { + if (!in_array($state, self::$validStates)) { + throw new \InvalidArgumentException('Invalid state given'); + } + } + + public function __toString(): string + { + return $this->state; + } +} diff --git a/Behavioral/Memento/Tests/MementoTest.php b/Behavioral/Memento/Tests/MementoTest.php index 722dbfa..d4a44f3 100644 --- a/Behavioral/Memento/Tests/MementoTest.php +++ b/Behavioral/Memento/Tests/MementoTest.php @@ -2,161 +2,30 @@ namespace DesignPatterns\Behavioral\Memento\Tests; -use DesignPatterns\Behavioral\Memento\Caretaker; -use DesignPatterns\Behavioral\Memento\Memento; -use DesignPatterns\Behavioral\Memento\Originator; +use DesignPatterns\Behavioral\Memento\State; +use DesignPatterns\Behavioral\Memento\Ticket; -/** - * MementoTest tests the memento pattern. - */ class MementoTest extends \PHPUnit_Framework_TestCase { - public function testUsageExample() + public function testOpenTicketAssignAndSetBackToOpen() { - $originator = new Originator(); - $caretaker = new Caretaker(); + $ticket = new Ticket(); - $character = new \stdClass(); - // new object - $character->name = 'Gandalf'; - // connect Originator to character object - $originator->setState($character); + // open the ticket + $ticket->open(); + $openedState = $ticket->getState(); + $this->assertEquals(State::STATE_OPENED, (string) $ticket->getState()); - // work on the object - $character->name = 'Gandalf the Grey'; - // still change something - $character->race = 'Maia'; - // time to save state - $snapshot = $originator->getStateAsMemento(); - // put state to log - $caretaker->saveToHistory($snapshot); + $memento = $ticket->saveToMemento(); - // change something - $character->name = 'Sauron'; - // and again - $character->race = 'Ainur'; - // state inside the Originator was equally changed - $this->assertAttributeEquals($character, 'state', $originator); + // assign the ticket + $ticket->assign(); + $this->assertEquals(State::STATE_ASSIGNED, (string) $ticket->getState()); - // time to save another state - $snapshot = $originator->getStateAsMemento(); - // put state to log - $caretaker->saveToHistory($snapshot); + // no restore to the opened state, but verify that the state object has been cloned for the memento + $ticket->restoreFromMemento($memento); - $rollback = $caretaker->getFromHistory(0); - // return to first state - $originator->restoreFromMemento($rollback); - // use character from old state - $character = $rollback->getState(); - - // yes, that what we need - $this->assertEquals('Gandalf the Grey', $character->name); - // make new changes - $character->name = 'Gandalf the White'; - - // and Originator linked to actual object again - $this->assertAttributeEquals($character, 'state', $originator); - } - - public function testStringState() - { - $originator = new Originator(); - $originator->setState('State1'); - - $this->assertAttributeEquals('State1', 'state', $originator); - - $originator->setState('State2'); - $this->assertAttributeEquals('State2', 'state', $originator); - - $snapshot = $originator->getStateAsMemento(); - $this->assertAttributeEquals('State2', 'state', $snapshot); - - $originator->setState('State3'); - $this->assertAttributeEquals('State3', 'state', $originator); - - $originator->restoreFromMemento($snapshot); - $this->assertAttributeEquals('State2', 'state', $originator); - } - - 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(); - - // still actual - $first_state->first_property = 1; - // just history - $second_state->second_property = 2; - $this->assertAttributeEquals($first_state, 'state', $originator); - $this->assertAttributeNotEquals($second_state, 'state', $originator); - - $originator->restoreFromMemento($snapshot); - // now it lost state - $first_state->first_property = 11; - // must be actual - $second_state->second_property = 22; - $this->assertAttributeEquals($second_state, 'state', $originator); - $this->assertAttributeNotEquals($first_state, 'state', $originator); - } - - public function testStateWithDifferentObjects() - { - $originator = new Originator(); - - $first = new \stdClass(); - $first->data = 'foo'; - - $originator->setState($first); - $this->assertAttributeEquals($first, 'state', $originator); - - $first_snapshot = $originator->getStateAsMemento(); - $this->assertAttributeEquals($first, 'state', $first_snapshot); - - $second = new \stdClass(); - $second->data = 'bar'; - $originator->setState($second); - $this->assertAttributeEquals($second, 'state', $originator); - - $originator->restoreFromMemento($first_snapshot); - $this->assertAttributeEquals($first, 'state', $originator); - } - - 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)); - } - - public function testCaretakerCustomLogic() - { - $caretaker = new Caretaker(); - $result = $caretaker->runCustomLogic(); - $this->assertEquals('State3', $result); + $this->assertEquals(State::STATE_OPENED, (string) $ticket->getState()); + $this->assertNotSame($openedState, $ticket->getState()); } } diff --git a/Behavioral/Memento/Ticket.php b/Behavioral/Memento/Ticket.php new file mode 100644 index 0000000..13f75e7 --- /dev/null +++ b/Behavioral/Memento/Ticket.php @@ -0,0 +1,49 @@ +currentState = new State(State::STATE_CREATED); + } + + public function open() + { + $this->currentState = new State(State::STATE_OPENED); + } + + public function assign() + { + $this->currentState = new State(State::STATE_ASSIGNED); + } + + public function close() + { + $this->currentState = new State(State::STATE_CLOSED); + } + + public function saveToMemento(): Memento + { + return new Memento(clone $this->currentState); + } + + public function restoreFromMemento(Memento $memento) + { + $this->currentState = $memento->getState(); + } + + public function getState(): State + { + return $this->currentState; + } +} diff --git a/Behavioral/Memento/uml/Memento.uml b/Behavioral/Memento/uml/Memento.uml new file mode 100644 index 0000000..194187a --- /dev/null +++ b/Behavioral/Memento/uml/Memento.uml @@ -0,0 +1,21 @@ + + + PHP + \DesignPatterns\Behavioral\Memento\Memento + + \DesignPatterns\Behavioral\Memento\State + \DesignPatterns\Behavioral\Memento\Memento + \DesignPatterns\Behavioral\Memento\Ticket + + + + + + + Fields + Constants + Methods + + private + + diff --git a/Behavioral/Memento/uml/Momento.uml b/Behavioral/Memento/uml/Momento.uml deleted file mode 100644 index c72667e..0000000 --- a/Behavioral/Memento/uml/Momento.uml +++ /dev/null @@ -1,22 +0,0 @@ - - - PHP - \DesignPatterns\Behavioral\Memento\Caretaker - - \DesignPatterns\Behavioral\Memento\Caretaker - \DesignPatterns\Behavioral\Memento\Originator - \DesignPatterns\Behavioral\Memento\Memento - - - - - - - Fields - Constants - Constructors - Methods - - private - - diff --git a/Behavioral/Memento/uml/uml.png b/Behavioral/Memento/uml/uml.png index 0fde074..417b293 100644 Binary files a/Behavioral/Memento/uml/uml.png and b/Behavioral/Memento/uml/uml.png differ diff --git a/Behavioral/Memento/uml/uml.svg b/Behavioral/Memento/uml/uml.svg index 2cc47a8..aeb665a 100644 --- a/Behavioral/Memento/uml/uml.svg +++ b/Behavioral/Memento/uml/uml.svg @@ -1,310 +1,952 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - state - - - - - - - - - - - - setState(state) - - - - - - - - - saveToMemento() - - - - - - - - - restoreFromMemento(memento) - - - - - - - - - - - - - Originator - - - Originator - - - - - - - - - - - - - - - - - - run() - - - - - - - - - - - - - Caretaker - - - Caretaker - - - - - - - - - - - - - - - - - - - state - - - - - - - - - - - - __construct(stateToSave) - - - - - - - - - - - - getState() - - - - - - - - - - - - - Memento - - - Memento - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + validStates + + + + + + + + + + + + + + + + state + + + + + + + + + + + + + + + + + + + STATE_CREATED + + + + + + + + + + + + + + + + STATE_OPENED + + + + + + + + + + + + + + + + STATE_ASSIGNED + + + + + + + + + + + + + + + + STATE_CLOSED + + + + + + + + + + + + + + + + ensureIsValidState(state) + + + + + + + + + + + + + __toString() + + + + + + + + + + + + + State + + + State + + + + + + + + + + + + + + + + + + + + + + + + + validStates + + + + + + + + + + + + + + + + state + + + + + + + + + + + + + + + + + + + STATE_CREATED + + + + + + + + + + + + + + + + STATE_OPENED + + + + + + + + + + + + + + + + STATE_ASSIGNED + + + + + + + + + + + + + + + + STATE_CLOSED + + + + + + + + + + + + + + + + + + + + + + ensureIsValidState(state) + + + + + + + + + + + + + + + + __toString() + + + + + + + + + + + + + State + + + State + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + state + + + + + + + + + + + + + + + + getState() + + + + + + + + + + + + + Memento + + + Memento + + + + + + + + + + + + + + + + + + + + + + state + + + + + + + + + + + + + + + + + + + getState() + + + + + + + + + + + + + Memento + + + Memento + + + + + + + + + + + + + + + + + + + + + + + + + currentState + + + + + + + + + + + + + + + + open() + + + + + + + + + + + + + assign() + + + + + + + + + + + + + close() + + + + + + + + + + + + + saveToMemento() + + + + + + + + + + + + + restoreFromMemento(memento) + + + + + + + + + + + + + getState() + + + + + + + + + + + + + Ticket + + + Ticket + + + + + + + + + + + + + + + + + + + + + + currentState + + + + + + + + + + + + + + + + + + + open() + + + + + + + + + + + + + + + + assign() + + + + + + + + + + + + + + + + close() + + + + + + + + + + + + + + + + saveToMemento() + + + + + + + + + + + + + + + + restoreFromMemento(memento) + + + + + + + + + + + + + + + + getState() + + + + + + + + + + + + + Ticket + + + Ticket + + +