![]() |
CS 635 Advanced Object-Oriented Design & Programming Spring Semester, 2004 Memento, Prototype & Abstract Factory |
|
---|---|---|
© 2004, All Rights Reserved, SDSU & Roger Whitney San Diego State University -- This page last updated 08-Mar-04 |
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:
Applicability
Use when you:
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; } }
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(); }
// 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; } }
Consequences/ Implementation
Simplifies Originator
You may be tempted to let the originator manage its state history
This adds to the complexity of the Originator
Defining Narrow and Wide Interfaces
C++
Class Memento { public: virtual ~Memento(); private: friend class Originator; Memento(); void setState(State*); State* GetState(); ...
Java[1]
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; } } }
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; } }
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"); } ...
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
Insurance Example
Insurance agents start with a standard policy and customize it
Two basic strategies:
Copying Issues
Shallow Copy Verse Deep Copy
Original Objects
![]()
Shallow Copy
![]()
Shallow Copy Verse Deep Copy
Original Objects
![]()
Deep Copy
![]()
Shallow Copy Verse Deep CopyOriginal Objects
![]()
Deeper Copy
![]()
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 ); }
How to in Java - Object clone()
protected Object clone() 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; }
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.
Consequences
Abstract Factory
Task - Write a cross platform window toolkit
GUI interfaces to run on
Create
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
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 } }
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
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() } }
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.
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.
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
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.
Applicability
Use when
Consequences
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?
[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