PHP Memento Design Pattern Part I: Wide & Narrow Interfaces

mementoWhere’s the Interface?!

In going through Design Patterns: Elements of Reusable Object Oriented Software I found only three design patterns with no abstract class or interface parents: The Singleton, The Facade, and The Memento. I quit using the Singleton after several articles and even Erich Gamma said it caused more problems than it solved, and I have not had an occasion to use the Facade in a practical application (yet). However, I used the Memento pattern in the development of a script that controlled a streaming video. When the user decided that he/she wanted to remember a certain part of the video, the Memento would save the position of the video and would go back to that position if requested. The purpose of the Memento is to capture an internal state and store that state in an external object. It is the ultimate “un-do” pattern. You can save any given state, store it and then go back to it using the Memento design pattern.

Ironically, the major concepts to understand in Memento development are those of wide and narrow interfaces. Further, the Gang of Four note that this may be tricky with languages that do not support two levels of static protection. (As far as I know, PHP does not have that support, but I’ve developed a workaround that does the trick.) So, without further ado, let’s take a look at the Memento. Run the program and download the files if you’d like:
PlayDownload

Saving State

The task of saving state is as simple as saving certain property values in a variable or array. For example, if you’re keeping score in a game, generally you just increment or decrement a variable. At some point you may wish to save a score and later on use it again. Let’s say that if a player reaches 100, you want to record that so that it can be used later in determining his/her character status. The Memento will do that for you and store a given state in a separate object, and the trick is to capture and retrieve that state without breaking encapsulation.

Without violating encapsulation, capture and externalize and object’s internal state so that the object can be restored to this state later.(GoF p. 283)

Figure 1 is the class diagram for that process:

Figure 1: Memento Design Pattern

Figure 1: Memento Design Pattern

It looks pretty simple, but it’s easy to get wrong, and if you look around on the internet, you’ll find plenty of bad examples. So let’s start with wide and narrow interfaces. (I thought you said it didn’t have interfaces!)

Wide and Narrow Interfaces

In most class diagrams, we will see a Client class—if not in the original class diagram, I generally put one in to use as a starting point. However, with the Memento, the Caretaker makes a request for a Memento from the Originator; so it acts like a Client. The Caretaker is supposed to act pretty much like a warehouse—it stores Memento objects, but it doesn’t alter their states. Then, upon request, it returns them.

The term “interface” in this context refers to the interface between classes. A “wide” interface will give the Originator access to the Memento’s private properties and methods, while a “narrow” interface will give the Caretaker no such access. In order to get mementos to store, the Caretaker must go through the Originator. The Originator gets the Memento instance for the Caretaker. But how do we insure that the Caretaker never changes state in the Memento? The solution I came up with is to use an Originator in Memento instantiation as shown in the following listing:


//Memento.php
class Memento
{
    //Memento state
    private $mstate;
 
    //Only the can be instantiated using Originator
    //object parameter. Originator's state is passed to 
    //Memento private method
    public function __construct(Originator $orig)
    {
         $this->setState($orig->state);
    }
 
    //Only accessable from within Memento
    private function setState($stateSetter)
    {
        $this->mstate=$stateSetter;
    }
 
    //No mutations in the getter
    public function getState()
    {
        return $this->mstate;
    }
}
?>

By sending an Originator instance to the Memento on instantiation insured by the use of an Originator code hint, it keeps any other object out of the Memento’s mutator private function—setState().The Caretaker stores Mementos by getting them from the Originator. So let’s look at the Originator:


//Originator.php
class Originator
{
    //state property must be public for
    //access by Memento
    public $state;
 
    //Request coming from Caretaker object
    public function createMemento()
    { 
       //Passes self ($this) to Memento
       return (new Memento($this));
    }
 
    public function setMemento(Memento $cm)
    {
       $this->state=$cm->getState();
    }    
 
    public function setState($mstate)
    {
        $this->state=$mstate;
    }
}
?>

The Originator participant has two key methods and a $state property. The one feature that bothers me is that the $state property is public, and if anyone can figure out a way to maintain Memento encapsulation with a private $state property in the Originator, let me know. As explained, in order to allow only the Originator to access the private methods of Memento, I incorporated the Originator into the Memento constructor parameter. Then, the Originator’s state is passed to Memento in a private method within Memento. The only way I could figure out how to do that is to make Originator $state public.

The two key methods that make up the Originator are createMemento() and setMemento(Memento). First, the createMemento() is used by the Caretaker objects to access Memento through Originator. Second, the setMemento(Memento) method is for restoring a saved state.(The setState(state) method is put in for simulating any state you may want to save, but it is not an essential part of the design pattern.) Here’s how it works:

A caretaker requests a memento from an originator, holds it for a time, and passes it back to the originator (GoF p. 286)

Because of the unique interaction between the participants, the Gang of Four provided an interaction diagram (pg. 286) that has been reconstructed in Figure 2:

Figure 2: Interaction diagram of Memento pattern

Figure 2: Interaction diagram of Memento pattern

In order to get a Memento object to store, the Caretaker object must request it through the Originator using the createMemento() method. In the instantiation process, the Originator object is passed to the private setState() method in the Memento where it’s own state is passed to the Memento’s state ($mstate).

To add a memento to the Caretaker, keeping in mind that the Caretaker has a narrow interface with the Memento, consider how the Caretaker’s addMemento (Memento $m) method works. It must get a Memento object from the Originator because to create a Memento object requires an Originator object in the Memento constructor. Then a private method in the Memento adds the state being saved. In requesting a saved memento state, the Caretaker just requests an array element from its own array; not the Memento. So the narrow interface between the Caretaker and Memento is through the Originator. None of this breaks encapsulation. The public method (other than the constructor function) in the Memento returns a state stored in a Memento private property. That value cannot be changed by the Caretaker (or any other object) through that public getter method; thereby preserving encapsulation.

To restore the state, I used a little different approach than GoF. Instead of a caretaker object calling the setMemento(Memento) method in the Originator, an originator object called setMemento(Memento) and then used the required memento object in the parameter to be a caretaker object method getMemento(n) to return the desired state stored in the Caretaker. Since the desired Memento is stored in an array element in Caretaker, I saw not breach of encapsulation using the approach that in this example. In looking at the Caretaker class, you can see how the getMemento(n) method works:


//Caretaker.php
class Caretaker
{
    private $storage=array();
 
    public function addMemento (Memento $m)
    {
        array_push($this->storage,$m);
    }
 
    public function getMemento ($index)
    {
        $stored=$this->storage[$index];
        return $stored;
    }
}
?>

Each memento can be accessed through a numeric index of the array, and in some applications you may choose to use an associative array instead; but it’s the same general process.

Using the Memento

For this post, I used a fairly trivial example of storing and retrieving mementos stored in memory. In the next post, I plan to have a practical example with a situation where you may need one. For now though, the Client class sets up and uses the necessary calls for storing and retrieving two mementos.


error_reporting(E_ALL | E_STRICT);
ini_set("display_errors", 1);
function __autoload($class_name) 
{
    include $class_name . '.php';
}
class Client
{
    //constructor function
    private $ct;
    private $orig;
    private $getBack;
    private $first;
    private $second;
    private $element;
    public function __construct()
    {
       $this->element=$_POST['restore'];
       $this->first=$_POST['first'];
       $this->second=$_POST['second'];
       $this->ct=new Caretaker();
       $this->orig = new Originator();
       $this->orig->setState($this->first);
       $this->ct->addMemento($this->orig->createMemento());
       $this->orig->setState($this->second);
       $this->ct->addMemento($this->orig->createMemento());
       $this->getBack=$this->orig->setMemento($this->ct->getMemento($this->element));
       echo $this->orig->state;
    } 
}
$worker=new Client();
?>

The HTML UI sends the data to be stored, and then two radio buttons are used to retrieve the memento of your choice:

?View Code HTML



    
    Storing States


Forget Me Not

Type whatever you want in the two Text boxes.
(Just make sure you write different things.)

 Type in something:
 Type in something else:

Which one do you want the Memento to recall?:
 First text
 Second text

@charset "UTF-8";
/* CSS Document */
/* Strategy UI */
/* 546574 blugray, EFF4EE off white, 91A56B moss green,
 * CDD8B1 lt dusty green, D68158 faded orange
*/
body
{
    font-family: Verdana, Helvetica, Arial, sans-serif;
    background-color: #EFF4EE;
    color: #546574;
}
 
h3
{
    color: #D68158;
    background-color:#CDD8B1;
    text-indent: 1em;
}
 
iframe
{
    background-color:#CDD8B1;
}

As you can see, the UI is quite simple and does nothing more than provide a way to send some data for the Memento pattern to use for demonstrating how the concept of memento works.

Head Trip

The Memento has a lot of lessons for understanding OOP and PHP. In preparing this post, I must have gone over the Memento chapter in GoF a dozen times and consulted other books on design patterns and online. I ran into one nasty fight where the author and commenter got into a heated debate over the whole process of encapsulation. Another commenter believed it could be solved by changing certain properties and operations from public to private. I’m not so sure.

One of the unique features of the Memento pattern is the whole concept of a wide and narrow interface between classes. The GoF recommend a w-i-d-e interface between the Originator and Memento participants a narrow one between the Memento and Caretaker. Add to that how GoF used C++ that has virtual visibility and friend classes, and work that out in PHP. There’s lots of head work, but it will give you interesting programming puzzles to solve. Next time, we’ll see how this can be used with a more practical application.

Copyright © 2014 William Sanders. All Rights Reserved.

2 Responses to “PHP Memento Design Pattern Part I: Wide & Narrow Interfaces”


  • Great approach!, maybe you can use…

    public function getState()
    {
    return $this->state;
    }

    …in your originator, so you can set private that $state and then replace your $orig->state by $orig->getState()

  • Zak,

    That sounds like a great idea. Did you give it a try? Originally when I was working on the Originator, certain properties had to be public because of interaction with the Memento, but I think your idea will work. In fact I think it’s a very bright idea.

    Kindest regards,
    Bill

Leave a Reply