From 314fc18556b46a0fc5e409a9e139c6ecef8c6e20 Mon Sep 17 00:00:00 2001 From: X25 Date: Sun, 13 Apr 2014 19:51:37 +0400 Subject: [PATCH 1/3] Implemented Memento Pattern --- Memento/Caretaker.php | 33 ++++++++++++++++++++++++++++++++ Memento/Memento.php | 25 ++++++++++++++++++++++++ Memento/Originator.php | 33 ++++++++++++++++++++++++++++++++ Memento/README.md | 18 ++++++++++++++++++ README.md | 1 + Tests/Memento/MementoTest.php | 36 +++++++++++++++++++++++++++++++++++ 6 files changed, 146 insertions(+) create mode 100644 Memento/Caretaker.php create mode 100644 Memento/Memento.php create mode 100644 Memento/Originator.php create mode 100644 Memento/README.md create mode 100644 Tests/Memento/MementoTest.php diff --git a/Memento/Caretaker.php b/Memento/Caretaker.php new file mode 100644 index 0000000..9c24357 --- /dev/null +++ b/Memento/Caretaker.php @@ -0,0 +1,33 @@ +setState("State1"); + //Setting state to State2 + $originator->setState("State2"); + //Saving State2 to Memento + $savedStates[] = $originator->saveToMemento(); + //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(); + //Setting state to State4 + $originator->setState("State4"); + + $originator->restoreFromMemento($savedStates[1]); + //State after restoring from Memento: State3 + } +} diff --git a/Memento/Memento.php b/Memento/Memento.php new file mode 100644 index 0000000..33e0dca --- /dev/null +++ b/Memento/Memento.php @@ -0,0 +1,25 @@ +state = $stateToSave; + } + + /** + * @return string + */ + public function getState() + { + return $this->state; + } +} diff --git a/Memento/Originator.php b/Memento/Originator.php new file mode 100644 index 0000000..6f1b9a7 --- /dev/null +++ b/Memento/Originator.php @@ -0,0 +1,33 @@ +state = $state; + } + + /** + * @return Memento + */ + public function saveToMemento() + { + return new Memento($this->state); + } + + public function restoreFromMemento(Memento $memento) + { + $this->state = $memento->getState(); + } +} diff --git a/Memento/README.md b/Memento/README.md new file mode 100644 index 0000000..83ca2c0 --- /dev/null +++ b/Memento/README.md @@ -0,0 +1,18 @@ +# Memento + +## Purpose + +Provide the ability to restore an object to its previous state (undo via rollback). + +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. + +## Examples + +* The seed of a pseudorandom number generator +* The state in a finite state machine diff --git a/README.md b/README.md index 56fd4df..2f9ba16 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ The patterns can be structured in roughly three different categories. Please cli * [Command](Command) [:notebook:](http://en.wikipedia.org/wiki/Command_pattern) * [Iterator](Iterator) [:notebook:](http://en.wikipedia.org/wiki/Iterator_pattern) * [Mediator](Mediator) [:notebook:](http://en.wikipedia.org/wiki/Mediator_pattern) +* [Memento](Memento) [:notebook:](http://http://en.wikipedia.org/wiki/Memento_pattern) * [NullObject](NullObject) [:notebook:](http://en.wikipedia.org/wiki/Null_Object_pattern) * [Observer](Observer) [:notebook:](http://en.wikipedia.org/wiki/Observer_pattern) * [Specification](Specification) [:notebook:](http://en.wikipedia.org/wiki/Specification_pattern) diff --git a/Tests/Memento/MementoTest.php b/Tests/Memento/MementoTest.php new file mode 100644 index 0000000..5fc8213 --- /dev/null +++ b/Tests/Memento/MementoTest.php @@ -0,0 +1,36 @@ +setState("State1"); + + $this->assertAttributeEquals("State1", "state", $originator); + + $originator->setState("State2"); + + $this->assertAttributeEquals("State2", "state", $originator); + + $savedState = $originator->saveToMemento(); + + $this->assertAttributeEquals("State2", "state", $savedState); + + $originator->setState("State3"); + + $this->assertAttributeEquals("State3", "state", $originator); + + $originator->restoreFromMemento($savedState); + + $this->assertAttributeEquals("State2", "state", $originator); + } +} From b9f5414f7d6cae8a2b4bb347028ab53db4df9917 Mon Sep 17 00:00:00 2001 From: x25 Date: Mon, 14 Apr 2014 16:43:25 +0400 Subject: [PATCH 2/3] typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2f9ba16..bfa3ab5 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ The patterns can be structured in roughly three different categories. Please cli * [Command](Command) [:notebook:](http://en.wikipedia.org/wiki/Command_pattern) * [Iterator](Iterator) [:notebook:](http://en.wikipedia.org/wiki/Iterator_pattern) * [Mediator](Mediator) [:notebook:](http://en.wikipedia.org/wiki/Mediator_pattern) -* [Memento](Memento) [:notebook:](http://http://en.wikipedia.org/wiki/Memento_pattern) +* [Memento](Memento) [:notebook:](http://en.wikipedia.org/wiki/Memento_pattern) * [NullObject](NullObject) [:notebook:](http://en.wikipedia.org/wiki/Null_Object_pattern) * [Observer](Observer) [:notebook:](http://en.wikipedia.org/wiki/Observer_pattern) * [Specification](Specification) [:notebook:](http://en.wikipedia.org/wiki/Specification_pattern) From 90696ad348a6647922d02291707f28d3221537bd Mon Sep 17 00:00:00 2001 From: X25 Date: Sat, 19 Apr 2014 17:20:54 +0400 Subject: [PATCH 3/3] Added state cloning if it is object --- Memento/Memento.php | 6 +++--- Memento/Originator.php | 8 +++++--- Tests/Memento/MementoTest.php | 37 +++++++++++++++++++++++++++++++++-- 3 files changed, 43 insertions(+), 8 deletions(-) diff --git a/Memento/Memento.php b/Memento/Memento.php index 33e0dca..358c037 100644 --- a/Memento/Memento.php +++ b/Memento/Memento.php @@ -4,11 +4,11 @@ namespace DesignPatterns\Memento; class Memento { - /* @var string */ + /* @var mixed */ private $state; /** - * @param string $stateToSave + * @param mixed $stateToSave */ public function __construct($stateToSave) { @@ -16,7 +16,7 @@ class Memento } /** - * @return string + * @return mixed */ public function getState() { diff --git a/Memento/Originator.php b/Memento/Originator.php index 6f1b9a7..726fbb3 100644 --- a/Memento/Originator.php +++ b/Memento/Originator.php @@ -4,14 +4,14 @@ namespace DesignPatterns\Memento; class Originator { - /* @var string */ + /* @var mixed */ private $state; // The class could also contain additional data that is not part of the // state saved in the memento.. /** - * @param string $state + * @param mixed $state */ public function setState($state) { @@ -23,7 +23,9 @@ class Originator */ public function saveToMemento() { - return new Memento($this->state); + $state = is_object($this->state) ? clone $this->state : $this->state; + + return new Memento($state); } public function restoreFromMemento(Memento $memento) diff --git a/Tests/Memento/MementoTest.php b/Tests/Memento/MementoTest.php index 5fc8213..d8a6ff7 100644 --- a/Tests/Memento/MementoTest.php +++ b/Tests/Memento/MementoTest.php @@ -5,12 +5,12 @@ namespace DesignPatterns\Tests\Memento; use DesignPatterns\Memento; /** - * MementoTest tests memento design pattern + * MementoTest tests the memento pattern */ class MementoTest extends \PHPUnit_Framework_TestCase { - public function testOriginator() + public function testStringState() { $originator = new Memento\Originator(); $originator->setState("State1"); @@ -33,4 +33,37 @@ class MementoTest extends \PHPUnit_Framework_TestCase $this->assertAttributeEquals("State2", "state", $originator); } + + public function testObjectState() + { + $originator = new Memento\Originator(); + + $foo = new \stdClass(); + $foo->data = "foo"; + + $originator->setState($foo); + + $this->assertAttributeEquals($foo, "state", $originator); + + $savedState = $originator->saveToMemento(); + + $this->assertAttributeEquals($foo, "state", $savedState); + + $bar = new \stdClass(); + $bar->data = "bar"; + + $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); + } }