SDSU CS 635 Advanced Object-Oriented Design & Programming
Spring Semester, 2004
Memento, Prototype & Abstract Factory
Previous    Lecture Notes Index    Next    
© 2004, All Rights Reserved, SDSU & Roger Whitney
San Diego State University -- This page last updated 08-Mar-04


References

Design Patterns: Elements of Reusable Object-Oriented Software, Gamma, Helm, Johnson, Vlissides, Addison-Wesley, 1995, pp. 283-292, 117-126, 87-96

The Design Patterns Smalltalk Companion, Alpert, Brown, Woolf, Addision-Wesley, 1998, pp. 297-304, 77-90, 31-46


Doc 12, Memento, Prototype & Abstract Factory Slide # 2

Memento


Store an object's internal state, so the object can be restored to this state later without violating encapsulation

Motivation

Allow undo, rollbacks, etc.

Structure



Only originator:




Doc 12, Memento, Prototype & Abstract Factory Slide # 3

Applicability


Use when you:




Doc 12, Memento, Prototype & Abstract Factory Slide # 4
An Example

package Examples;
class Memento
   {
   private Hashtable savedState = new Hashtable();
   
   protected Memento() {}; //Give some protection
   
   protected void setState( String stateName, Object stateValue )
      { 
      savedState.put( stateName, stateValue );
      } 
   
   protected Object getState( String stateName)
      {
      return savedState.get( stateName);
      } 
      
   protected Object getState(String stateName, Object defaultValue )
      {
      if ( savedState.containsKey( stateName ) )
         return savedState.get( stateName);
      else
         return defaultValue;
      } 
   }



Doc 12, Memento, Prototype & Abstract Factory Slide # 5
A Class whose state is saved

package Examples;
class ComplexObject
   {
   private String name;
   private int someData;
   private Vector objectAsState = new Vector();
   
   public Memento createMemento()
      {
      Memento currentState = new Memento();
      currentState.setState( "name", name );
      currentState.setState( "someData", new Integer(someData) );
      currentState.setState( "objectAsState", objectAsState.clone() );
      return currentState;
      }
   
   public void restoreState( Memento oldState)
      {
      name = (String) oldState.getState( "name", name );
      objectAsState = (Vector) oldState.getState( "objectAsState" );
      Integer data = (Integer) oldState.getState( "someData");
      someData = data.intValue();
      }




Doc 12, Memento, Prototype & Abstract Factory Slide # 6
   // Show a way to do incremental saves
   public Memento setName( String aName )
      {
      Memento deltaState = saveAState( "name", name);
      name = aName;
      return deltaState;
      }


   public void setSomeData( int value )
      {
      someData = value;
      }
   
   private Memento saveAState(String stateName, Object stateValue)
      {
      Memento currentState = new Memento();
      currentState.setState( stateName, stateValue );
      return currentState;
      }   
   }



Doc 12, Memento, Prototype & Abstract Factory Slide # 7

Consequences/ Implementation


Simplifies Originator

You may be tempted to let the originator manage its state history

This adds to the complexity of the Originator


Using Mementos might be expensive

Copying state takes time and space

If this takes too much time/space pattern may not be appropriate

Preserve encapsulation boundaries

Give Memento two interfaces: wide and narrow

Let originator have access to all set/get/state of Memento

Let others only hold Mementos and destroy them


Doc 12, Memento, Prototype & Abstract Factory Slide # 8
Defining Narrow and Wide Interfaces

C++

Make Memento's interface private
Make Originator a friend of the Memento
   Class Memento {
   public:
      virtual ~Memento();
   private:
      friend class Originator;
      Memento();
      void setState(State*);
      State* GetState();
      ...


Doc 12, Memento, Prototype & Abstract Factory Slide # 9
Java[1]

Use private nested/inner class to hide memento's interface

class ComplexObject {
   private String name;
   private int someData;
   
   public Memento createMemento() {
      return new Memento();
   }
      
   public void  restoreState( Memento oldState) {
      oldState.restoreStateTo( this );
   }
   
   public class Memento {
      private String savedName;
      private int savedSomeData;
      
      private Memento() {
         savedName = name;
         savedSomeData = someData;
      }
      
      private void restoreStateTo(ComplexObject target) {
         target.name = savedName;
         target.someData = savedSomeData;
      }
   }
}


Doc 12, Memento, Prototype & Abstract Factory Slide # 10
Using Clone to Save State

One can wrap a clone of the Originator in a Memento or

Just return the clone as a type with no methods

interface Memento extends Cloneable { }

class ComplexObject implements Memento {
   private String name;
   private int someData;
   
   public Memento createMemento() {
      Memento myState = null;
      try {
         myState =  (Memento) this.clone();
      }
      catch (CloneNotSupportedException notReachable) {
      }
      return myState;
   }
      
   public void  restoreState( Memento savedState) {
      ComplexObject myNewState = (ComplexObject)savedState;
      name = myNewState.name;
      someData = myNewState.someData;
   }
}


Doc 12, Memento, Prototype & Abstract Factory Slide # 11

Iterators & Mementos


Using a Memento we can allow multiple concurrent iterations

class IteratorState {
   int currentPosition = 0;
   
   protected IteratorState() {}
   
   protected int getPosition() {   return currentPosition;   }
   
   protected void advancePosition() { currentPosition++; }
   }
   
class Vector {
   protected Object elementData[];
   protected int elementCount;
   
   public IteratorState newIteration() { return new IteratorState(); }
   
   public boolean hasMoreElements(IteratorState aState) {
      return aState.getPosition() < elementCount;
   }
   
   public Object nextElement( IteratorState aState ) {
      if (hasMoreElements( aState ) ) {
         int currentPosition = aState.getPosition();
         aState.advancePosition();
         return elementData[currentPosition];
         }
   throw new NoSuchElementException("VectorIterator");
   }
   ...


Doc 12, Memento, Prototype & Abstract Factory Slide # 12

Prototype

Intent


Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype


Applicability


Use the Prototype pattern when




It may be easier to have the proper number of prototypes and clone them rather than instantiating the class manually each time


Doc 12, Memento, Prototype & Abstract Factory Slide # 13
Insurance Example

Insurance agents start with a standard policy and customize it



Two basic strategies:




Doc 12, Memento, Prototype & Abstract Factory Slide # 14

Copying Issues

Shallow Copy Verse Deep Copy


Original Objects


Shallow Copy




Doc 12, Memento, Prototype & Abstract Factory Slide # 15

Shallow Copy Verse Deep Copy

Original Objects


Deep Copy




Doc 12, Memento, Prototype & Abstract Factory Slide # 16
Shallow Copy Verse Deep CopyOriginal Objects



Deeper Copy



Doc 12, Memento, Prototype & Abstract Factory Slide # 17

Cloning Issues

How to in C++ - Copy Constructors

class Door 
   {
   public:
      Door();
      Door( const Door&);
   
      virtual Door* clone() const;
   
      virtual void Initialize( Room*, Room* );
      // stuff not shown
   private:
      Room* room1;
      Room* room2;
   }
   
Door::Door ( const Door& other ) //Copy constructor
   {
   room1 = other.room1;
   room2 = other.room2;
   }
   
Door* Door::clone()  const 
   {
   return new Door( *this );
   }



Doc 12, Memento, Prototype & Abstract Factory Slide # 18
How to in Java - Object clone()

protected Object clone() throws CloneNotSupportedException

Default is shallow copy

Returns: A clone of this Object.

Throws: OutOfMemoryError


Throws: CloneNotSupportedException


class Door implements Cloneable {
   public void Initialize( Room a, Room b) 
      { room1 = a; room2 = b; }
   public Object clone() throws
                CloneNotSupportedException {
      // modify this method for deep copy
      // no need to implement this method for shallow copy
      return super.clone();
   }
   Room room1;
   Room room2;
}


Doc 12, Memento, Prototype & Abstract Factory Slide # 19
VisualWorks Smalltalk

Object>>shallowCopy
   Does a shallowCopy of the receiver

Object>>copy
   ^self shallowCopy postCopy
   
   “Template method for copy”

Copy is the primary method for copying an object

Classes override postCopy to do more than shallow copyDoor

Smalltalk.CS635 defineClass: #Door
   superclass: #{Core.Object}
   indexedType: #none
   private: false
   instanceVariableNames: 'room1 room2 '

postCopy
   room1 := room1 copy.
   room2 := room2 copy. 


Doc 12, Memento, Prototype & Abstract Factory Slide # 20

Consequences








Implementation Issues






Doc 12, Memento, Prototype & Abstract Factory Slide # 21

Abstract Factory


Task - Write a cross platform window toolkit

GUI interfaces to run on

We will look at widgets: Windows, Menu's and Buttons


Doc 12, Memento, Prototype & Abstract Factory Slide # 22
Create






Doc 12, Memento, Prototype & Abstract Factory Slide # 23
This allows the application to write to the widget interface

public void installDisneyMenu()
   {
   Menu disney = create a menu somehow
   disney.addItem( "Disney World" );
   disney.addItem( "Donald Duck" );
   disney.addItem( "Mickey Mouse" );
   disney.addGrayBar( );
   disney.addItem( "Minnie Mouse" );
   disney.addItem( "Pluto" );
   etc.
   }

How to create the widget so





Doc 12, Memento, Prototype & Abstract Factory Slide # 24
Use Abstract Factory

abstract class WidgetFactory
   {
   public Window createWindow();
   public Menu createMenu();
   public Button createButton();
   }
   
class MacWidgetFactory extends WidgetFactory
   {
   public Window createWindow() 
      { code to create a mac window }
      
   public Menu createMenu() 
      { code to create a mac Menu }
      
   public Button createButton() 
      { code to create a mac button }
   }   
   
class Win95WidgetFactory extends WidgetFactory
   {
   public Window createWindow() 
      { code to create a Win95 window }
      
   public Menu createMenu() 
      { code to create a Win95 Menu }
      
   public Button createButton() 
      { code to create a Win95 button }
   }


Doc 12, Memento, Prototype & Abstract Factory Slide # 25
Now to get code that works for all platforms we get:

public void installDisneyMenu(WidgetFactory myFactory)
   {
   Menu disney = myFactory.createMenu();
   disney.addItem( "Disney World" );
   disney.addItem( "Donald Duck" );
   disney.addItem( "Mickey Mouse" );
   disney.addGrayBar( );
   disney.addItem( "Minnie Mouse" );
   disney.addItem( "Pluto" );
   etc.
   }

We just need to make sure that the application for each platform creates the proper factory


Doc 12, Memento, Prototype & Abstract Factory Slide # 26

How Do Factories create Widgets?

Method 1) My Factory Method


abstract class WidgetFactory
   {
   public Window createWindow();
   public Menu createMenu();
   public Button createButton();
   }
   
class MacWidgetFactory extends WidgetFactory
   {
   public Window createWindow() 
      { return new MacWidow() }
      
   public Menu createMenu() 
      { return new MacMenu() }
      
   public Button createButton() 
      { return new MacButton()  }
   }   



Doc 12, Memento, Prototype & Abstract Factory Slide # 27
How Do Factories create Widgets?

Method 2) Their Factory Method


abstract class WidgetFactory {
   private Window windowFactory;
   private Menu menuFactory;
   private Button buttonFactory;
   
   public Window createWindow()
      { return windowFactory.createWindow() }
   
   public Menu createMenu();
      { return menuFactory.createMenu() }
   
   public Button createButton()
      { return buttonFactory.createMenu() }
}
   
class MacWidgetFactory extends WidgetFactory {
   public MacWidgetFactory() {
      windowFactory = new MacWindow();
      menuFactory = new MacMenu();
      buttonFactory = new MacButton();
   }
}   
   
class MacWindow extends Window {
   public Window createWindow() { blah }
   etc.


Doc 12, Memento, Prototype & Abstract Factory Slide # 28
Method 2) Their Factory Method

When does this make Sense?

There might be more than one way to create a widget

abstract class WidgetFactory {
   private Window windowFactory;
   private Menu menuFactory;
   private Button buttonFactory;
   
   public Window createWindow()
      { return windowFactory.createWindow() }
   
   public Window createWindow( Rectangle size)
      { return windowFactory.createWindow( size ) }
   
   public Window createWindow( Rectangle size, String title)
      { return windowFactory.createWindow( size, title) }
   
   public Window createFancyWindow()
      { return windowFactory.createFancyWindow() }
   
   public Window createPlainWindow()
      { return windowFactory.createPlainWindow() }

Using factory method allows abstract class to do all the different ways to create a window.

Subclasses just provide the objects windowFactory, menuFactory, buttonFactory, etc.


Doc 12, Memento, Prototype & Abstract Factory Slide # 29
How Do Factories create Widgets?

Method 2.5) Subclass returns Class


abstract class WidgetFactory {
   
   public Window createWindow()
      { return windowClass().newInstance() }
   
   public Menu createMenu();
      { return menuClass().newInstance()  }
   
   public Button createButton()
      {  return buttoneClass().newInstance() }
   
   public Class windowClass();
   public Class menuClass();
   public Class buttonClass();
}
   
class MacWidgetFactory extends WidgetFactory {
   public Class windowClass() 
      { return MacWindow.class; }
   
   public Class menuClass() 
      { return MacMenu.class; }
   
   public Class buttonClass() 
      { return MacButton.class; }
}   
Smalltalk practice
Parent class normally does more complex stuff

Doc 12, Memento, Prototype & Abstract Factory Slide # 30
How Do Factories create Widgets?

Method 3) Prototype


class WidgetFactory
   {
   private Window windowPrototype;
   private Menu menuPrototype;
   private Button buttonPrototype;
   
   public WidgetFactory( Window windowPrototype,
                  Menu menuPrototype,
                  Button buttonPrototype)
      {
      this.windowPrototype = windowPrototype;
      this.menuPrototype = menuPrototype;
      this.buttonPrototype = buttonPrototype;
      }
   
   public Window createWindow()
      { return windowFactory.createWindow() }
   
   public Window createWindow( Rectangle size)
      { return windowFactory.createWindow( size ) }
   
   public Window createWindow( Rectangle size, String title)
      { return windowFactory.createWindow( size, title) }
   
   public Window createFancyWindow()
      { return windowFactory.createFancyWindow() }
   
   etc.

There is no need for subclasses of WidgetFactory.


Doc 12, Memento, Prototype & Abstract Factory Slide # 31

Applicability


Use when






Doc 12, Memento, Prototype & Abstract Factory Slide # 32
Consequences






Implementation




Doc 12, Memento, Prototype & Abstract Factory Slide # 33
Problem: Cheating Application Code

public void installDisneyMenu(WidgetFactory myFactory)
   {
   // We ship next week, I can't get the stupid generic Menu 
   // to do the fancy Mac menu stuff
   // Windows version won't ship for 6 months
   // Will fix this later
   
   MacMenu disney = (MacMenu) myFactory.createMenu();
   disney.addItem( "Disney World" );
   disney.addItem( "Donald Duck" );
   disney.addItem( "Mickey Mouse" );
   disney.addMacGrayBar( );
   disney.addItem( "Minnie Mouse" );
   disney.addItem( "Pluto" );
   etc.
   }

How to avoid this problem?


Doc 12, Memento, Prototype & Abstract Factory Slide # 34
[1] RestoreStateTo does not access the fields of the outer object in case one wants to restore the state to a different ComplexObject object. One may wish to use an nested class to avoid tangling the memento to the outer object

Copyright ©, All rights reserved.
2004 SDSU & Roger Whitney, 5500 Campanile Drive, San Diego, CA 92182-7700 USA.
OpenContent license defines the copyright on this document.

Previous    visitors since 08-Mar-04    Next