chapter 19 memento
DESCRIPTION
Chapter 19 Memento. Summary prepared by Kirk Scott. Design Patterns in Java Chapter 19 Memento. Summary prepared by Kirk Scott. The Introduction Before the Introduction. This is a succinct preview summary of the memento examples: - PowerPoint PPT PresentationTRANSCRIPT
1
Chapter 19Memento
Summary prepared by Kirk Scott
2
3
4
5
Design Patterns in JavaChapter 19Memento
Summary prepared by Kirk Scott
6
The Introduction Before the Introduction
• This is a succinct preview summary of the memento examples:
• 1. You can save the state of a running application in a stack data structure
• 2. You can also save the state of an application in secondary storage using serializability
7
• It’s worth noting again that there is some utility in the authors’ emphasis on intent
• Structurally, there is no close relationship between a stack and serializability
• However, in their respective contexts, transient vs. persistent storage, they can both be used in order to accomplish the same thing
8
• The book’s examples are based on adding an undo feature to the graphic visualization of a factory
• Whenever a change is to be made to the state of the visualization, a copy of the current state is saved before the change is made
• When the undo option is selected, the current state is replaced by the most recently saved state
9
• Book definition:• The intent of the Memento pattern is to
provide storage and restoration of an object’s state
10
A Classic Example: Using Memento for Undo
• A memento may be as simple as a single object that holds an application’s state
• It may also turn out that some collection of objects or some data structure (like a stack) is involved in implementing the memento pattern
11
• From a practical point of view, these kinds of questions have to be answered:
• How will state be captured in a memento?• When will the state be captured?• How will the previous state be restored based
on the contents of the memento?
12
• On the next overhead the graphical visualization of a factory is shown at software start-up
• Its state is currently empty—but it’s important to note that empty is a state
• Since nothing has been done yet, the Undo button is disabled
13
14
• The next overhead shows the application after a few machines have been added and dragged around the work area
• Note that the Undo button is no longer disabled
15
16
• The scenario under discussion is one of run-time functionality
• The state that needs to be captured is a list of the locations of the machines that the user has placed in the application
• One such list is a memento• A history of multiple states is stored in a stack• Clicking the Undo button means popping the
stack
17
• In particular, the book outlines the functionality in this way:
• 1. Each time the user takes an action like add or move, a memento will be created and pushed onto the stack
• 2. Each time the user clicks Undo, the stack will be popped, removing the current state, and the state will be restored to the previous state, which is now at the top of the stack
18
• At start-up time, a memento of the empty state will be pushed onto the stack
• The visualization always has a state and the stack will never be empty
• The Undo button will be disabled when the stack only has a single entry in it
• It will not be possible to pop the empty state off of the stack
19
• Comment mode on:• The book hasn’t stated how the list of
machine locations will be saved yet, but strictly speaking, such a list is the literal memento in the application
• The stack is used in order to manage a collection of potentially more than one memento
20
• Comment mode, continued:• The book’s approach may not be elegant, but
it’s straightforward• Notice that they aren’t trying to save a list of
actions, for example• This means that it’s not necessary to figure
out how to undo an action
21
• Comment mode, continued:• Instead, complete states are saved• Then undoing simply means restoring the
previous state• I would note that there seems to be a lot of
redundancy in storing all of the previous states in this way
22
• I suspect that it would be possible to model state as a sequential collection of machines and a sequence of actions taken on them
• This might result in less redundancy among the mementos
• The code for undoing would be considerably more complex than simply restoring the list of previous machine locations
23
What the Book Does Next
• In keeping with the plan of previous chapters, the book develops the application step-by-step
• It gives an initial solution• It then modifies that solution• The solutions illustrate a particular coding
technique which isn’t part of the pattern but which deserves explanation
24
• In general, this is what should happen when the user interacts with the visualization:
• When a machine is added or moved, the factory model should change accordingly,
• The appearance of the visualization should be updated,
• The state should be saved
25
• When the user clicks Undo the previous state should be restored to the model
• The appearance of the visualization should also be updated when this happens
• The application reacts to events by means of a mouse listener in the work area and listeners for the Add and Undo buttons
26
• Notice how this problem description seems like it might be ripe for an MVC solution
• In its solutions the book doesn’t use listener and observer like in full MVC
• However, it does use listening in order to achieve its goal
• (You could argue whether the implementation is “MVC in spirit”—which may be a dangerous oxymoron.)
27
The Book’s First Solution
• On the next overhead, a UML diagram of the FactoryModel class is shown with references to instances of the system supplied Stack and ArrayList classes
• The stack holds the mementos• The instance of ArrayList holds the listeners for
the graphical components of the application• This second point will merit discussion
28
29
The Book’s Coding Technique Applied to this Problem
• Ultimately, it’s the stack of mementos that is of real interest
• However, there is no escaping the ArrayList of listeners in the design
• Notice that the FactoryModel class also implements methods named addChangeListener() and notifyListeners()
30
• It is neither inherently good or bad to gather listeners together in an ArrayList
• We’re seeing something of how listeners are managed in system supplied classes only in programmer written code
• From the perspective of the previous unit, the important point is that we have a GUI, but we are not using the Observer design pattern in order to an MVC application
31
• The diagram hasn’t broken out the view yet• But from this initial diagram alone, it is
apparent that the model and the controller functionality are intertwined
• You might say the authors have implemented an “MVC design in spirit”, although that might be an oxymoron
32
• In any case, the first task is to be clear about how the ArrayList of listeners and the addChangeListener() and notifyListeners() methods work
• After that we’ll take a look at how the memento functionality works
• Then we’ll move on from this example to the next, which should be a better solution
33
The ArrayList of Listeners
• On the next overhead those parts of FactoryModel having to do directly with the ArrayList of listeners is shown
34
• public class FactoryModel {• …• private ArrayList listeners = new ArrayList();• …• public void addChangeListener(ChangeListener listener)
{• listeners.add(listener);• }• …• public void notifyListeners() {• for (int i = 0; i < listeners.size(); i++)• ((ChangeListener)
listeners.get(i)).stateChanged(new ChangeEvent(this));• }
35
• By way of introductory explanation, what they authors have done is make FactoryModel “listenable”
• Making it observable would have been syntactically clear and better
• The point is that there is no such thing as listenability except by “inventing” it
36
• That’s what they’ve done here• You don’t extend a listenable class• You simply implement methods that make it
possible to have a collection of listeners associated with FactoryModel
• Then look at the notifyListeners() method• You explicitly call stateChanged() on each
associate listener
37
• In other words, you’re not relying on a callback sequence
• You’re coding it yourself• The more I look at this the less I like it
38
• I’m getting ahead of myself, because we haven’t discussed how the mementos actually work yet, but this is a call that changes the state by adding a memento
• The point is, this is where a call to notifyListeners() is located
• public void setLocations(ArrayList list) • {• mementos.push(list);• notifyListeners();• }
39
• In short, in an MVC design, when the model/state/mementos changed, you would observe that fact and the observers would “act”
• Here, we’re short-circuiting this by notifying listeners to act when the state changes
40
• The remaining question is, what is or are the actual listeners for this application?
• What do they do when the state of the FactoryModel changes and they are notified?
• We won’t arrive at the answer to this question until later…
41
• Anyway, this is the general design for how the authors have implemented their example
• In order to follow the rest of the explanation of the handling of mementos, it’s simply necessary to accept this and see what else they have done
42
Implementing Memento in the First Example
• What is a memento?• It is an ArrayList which holds the points
specifying the locations of the machines in the work area for a given application state
• The stack in the FactoryModel potentially holds multiple instances of such ArrayLists
43
• Here is the code at the beginning of the FactoryModel class
• It shows the declarations of the Stack of mementos and the ArrayList of listeners
• It also shows the default constructor• In it the Stack is constructed and the initial,
empty state is constructed and pushed onto it
44
• public class FactoryModel• {• private Stack mementos;• private ArrayList listeners = new ArrayList();• public FactoryModel()• {• mementos = new Stack();• mementos.push(new ArrayList());• }• …
45
• Most of the code shown is unremarkable• The most important thing to notice is this line of
code:• mementos.push(new ArrayList());• A memento is an instance of ArrayList• When the model is constructed, the empty state is
put onto the stack• The default constructor is used to create an empty
ArrayList and push it onto the stack
46
• Notice that in general, there is no need for a state to be named
• The states are not named instances of objects• They are simply instances of ArrayList
constructed at the moment
47
Ordering of mementos
• The identifying feature of states which is important to the application is where in the stack they occur, since a stack is an ordered data structure
• In other words, going down the stack means going back in time
48
• The book summarizes the rest of the methods in the FactoryModel class as follows:
• They maintain the stack of mementos• They deal with events that make changes
49
Adding a Machine to the Representation
• Code for the FactoryModel add() method is shown on the following overhead, for example
• Adding a machine to the visualization means adding a point to the list of machine locations
• When this change occurs, the listeners are notified
• Comments have been added to the code
50
• public void add(Point location)• {• /* Get a copy of the old state by peeking at the top of
the stack of mementos. */• List oldLocs = (List) mementos.peek();• /* Create a new state, the same as the old. */• List newLocs = new ArrayList(oldLocs);• /* Add the new location to the new state. */• newLocs.add(0, location);• /* Push the new state onto the stack. */• mementos.push(newLocs);• /* Notify the listeners of changes to the state. */• notifyListeners();• }
51
Ordering in Mementos
• It is possible to iterate over lists without worrying about the order of their contents
• It is also possible to refer to elements of the list by index
• In the previous method, a location was added to a memento ArrayList at position 0
52
• What’s happening here is a version of the painter’s algorithm
• Each succeeding machine and location that becomes part of a state is put “on top”
• That way, if visual representations overlap, the thing that is “on top” of the ArrayList will be displayed on top of the other representations in the GUI
53
Getting All Locations for a Current State
• The code for the getLocations() method is given on the following overhead
• Responding to events and repainting the work area will require access to all of the current machine locations
• The method returns the list of current machine locations—the list at the top of the memento stack
• Notice that peek() is not the same as popping the stack
54
• public List getLocations()• {• return (List) mementos.peek();• }
55
Implementing Undo Functionality
• Next the book considers the undo() method in the FactoryModel class
• This is the method that triggers a change in state back to the previous one
• Like the add() method given above, the undo() method will notify any listeners
• The book deals with this method as a challenge
56
• Challenge 19.1• Write the code for the FactoryModel class’s
undo() method.
57
• Solution 19.1• Here is one implementation of undo() for
FactoryModel• [See the code on the following overhead• Comment mode on:• It is clear that these two methods could easily be
merged• The reason for a separate canUndo() will become
evident later]
58
• public boolean canUndo()• {• return mementos.size() > 1;• }• public void undo()• {• if(!canUndo()) return;• mementos.pop();• notifyListeners();• }
59
• Solution 19.1, continued:• This code is careful to ignore undo() requests
if the stack is down to its initial state with a single memento.
• The top of the stack is always the current state, so the undo() code has to pop the stack to expose the previous memento.
60
• Comment mode on:• Note that popping the stack means throwing
away the current state, because you don’t capture the state that is popped
• You’re not supporting redo functionality• We don’t have time to worry about it, but
thinking about it suggests that supporting both undo and redo would be an interesting challenge
61
• Solution 19.1, continued:• When you write a createMemento() method,
you should convince yourself or your colleagues that the method returns all the information necessary to reconstruct the receiving object.
• In this example, a machine simulator can reconstruct itself from a clone, and a factory simulator can reconstruct itself from a list of machine simulator clones.
62
• Solution 19.1• Comment mode on• What the authors are trying to say in the previous
paragraph is not clear• They have claimed that the only memento you need for
this application is a list of machine locations• Presumably, the picture of each machine in the
visualization is just a stock image• Why they’re now babbling about clones and lists of
clones is not clear
63
• Back to the material in the text:• The previous UML diagram and code examples
gave some idea of the FactoryModel class• It contains methods that change the state of the
model• It also contains methods to register listeners and
notify them when the state changes• For better or worse, we still haven’t been told
about the listeners
64
• The book suggests that overall, the visualization can be structured using a model-view-controller type design
• A UML diagram is given on the following overhead showing how the visualization and the factory model are implemented in separate classes
• The meaning of the VisMediator class and the various relationships shown will be outlined afterwards
65
66
• The application reacts to events by means of a mouse listener in the work area and listeners for the Add and Undo buttons
• Part of the role of the VisMediator class is to hold the functionality that responds to events
• In other words, the VisMediator contains the controller functionality
67
• An expanded UML diagram showing the relationships between the FactoryModel, Visualization, and VisMediator classes will be shown shortly
• The Visualization class has methods related to creating the GUI of the application
• It can respond to changes in the factory model in order to update the graphical representation
68
• The VisMediator class is the locus for listening• In other words, handling GUI events is offloaded
into this new class• The book expresses the function of the
VisMediator class in this way:• “The mediator translates GUI events into factory
changes.”• The relationships are shown in the UML diagram
on the following overhead
69
70
What the Book Has and Has Not Done
• It is important to note that for all practical purposes in this new design there has been no change in the FactoryModel
• In other words, it will still have listeners added to it
• And when you look at the Visualization class, it contains a stateChanged() method
• Although it’s not shown in the UML diagram, it effectively implements ChangeListener
71
• The overall story is this:• Listening for controller functionality has been
offloaded into VisMediator• However, the view side of this design is still
driven by listening rather than observing• This is moderately disappointing since the
book introduces Observer in chapter 9 and Memento in chapter 19
72
Code for the New Example
• The book now shows the relationship between the Visualization class and the VisMediator classes
• The Visualization class has a reference to the VisMediator class
• The Visualization class has a method to create an Undo button and adds to it a listener belonging to the VisMediator class
• This is shown on the next overhead
73
• protected JButton undoButton()• {• if(undoButton == null)• {• undoButton = ui.createButtonCancel();• undoButton.setText(“Undo”);• undoButton.setEnabled(false);• undoButton.addActionListener(mediator.undoAction());• }• return undoButton;• }
74
• In summary, the previous code shows that the mediator is the listener for a button in the Visualization GUI
75
• The book states that the mediator then informs the factory of requested changes using the code shown on the following overhead
• VisMediator has a reference to the FactoryModel which it received as a construction parameter
• VisMediator has a button listener• The button listener calls the undo() method shown• This method wraps a call to a method named undo() in
the FactoryModel class which actually changes the model
76
• private void undo(ActionEvent e)• {• factoryModel.undo();• }
77
• The constructor for Visualization is shown on the following overhead
• The third line of code shows how the VisMediator is constructed with a factory model reference and how the Visualization receives a reference to this
• The fourth line of code shows the Visualization being added to the factory model as a change listener
78
• public Visualization(UI ui)• {• super(new BorderLayout));• this.ui = ui;• mediator = new
VisMediator(factoryModel);• factoryModel.addChangeListener(this);• add(machinePanel(),
BorderLayout.CENTER);• add(buttonPanel(), BorderLayout.SOUTH);• }
79
How It Works, Again
• The VisMediator listens to the GUI• The VisMediator has access to change the
FactoryModel• The Visualization listens to the FactoryModel• A change in the FactoryModel is propagated to
the Visualization by calling the stateChanged() method on it
80
• The sequence of events when the Undo button is clicked in the GUI can be modeled as shown in the following sequence diagram
81
82
• A state change, like undo, means that all of the current pictures of machines have to be removed from the Visualization
• Then pictures at all of the locations in the previous state have to be presented
• The Undo button has to be enabled/disabled according to whether a further undo is possible
83
• Challenge 19.2• Write the stateChanged() method for the
Visualization class.
84
• Solution 19.2• One solution is:• [See code on the next overhead.]
85
• public void stateChanged(ChangeEvent e)• {• machinePanel().removeAll();• List locations = factoryModel.getLocations();• for(int i = 0; i < locations.size(); i++)• {• Point p = (Point) locations.get(i);• machinePanel().add(createPictureBox(p));• }• undoButton().setEnabled(factoryModel.canUndo());• repaint();• }
86
• Note three things in the previous code:• 1. When generating the visual representation,
the list of locations is traversed in order• In other words, a for loop, not a for each loop
is used• This makes sure that the most recent machine
images are on top
87
• 2. Also, there is a createPictureBox() method that we haven’t seen before
• This apparently is the method that generates the standard representation of a machine in the visualization
• 3. This is when you find out why they implemented canUndo() earlier as a separate method—so it can be called here
88
• The book’s summary of the foregoing example is this:
• Mementos, records of visualization state, are being stored as objects in a stack while the program is running
• It is also possible that more persistent storage of state may be desirable
89
Memento Durability
• This subsection can be summarized very succinctly
• If you want persistent storage of state, in Java the solution is to save memento objects in secondary storage using serializability
• In the interests of completeness, the book’s observations and examples will be presented
90
• The book observes that it may be desirable to store state over widely varying periods of time
• The previous example was about storing it during the course of a program run
• A program run can be referred to as a session
91
• If state is to be stored between sessions, the period of time involved could range from days, to weeks, to months, to years…
• State can be saved in the form of a persistent serializable object, or perhaps a text string or other data in secondary storage
92
• Challenge 19.3• Write down two reasons that might drive you
to save a memento in a file rather than as an [a runtime] object.
93
• Solution 19.3• Storing a memento as an object assumes that
the application will still be running when the user wants to restore the original object.
• Reasons that will force you to save a memento to persistent storage include the following.
94
• The ability to restore an object’s state has to survive a system crash.
• You anticipate that the user will exit the system and will want to resume work later.
• You need to reconstruct an object on another computer.
• [End of Solution 19.3.]
95
Persisting Mementos Across Sessions
• The book next proposes a new visualization, Visualization2, which is a subclass of the original visualization
• Visualization2 will have menu options to “Save As…” and “Restore From…”
• These are analogous to the save and load features of Togiz Kumalak from CSCE 202
96
• The domain specific difference is simply in what’s being saved and restored
• There are also potentially minor syntactical differences in how the authors implement the listeners compared to the model used by the orange book (CSCE 202)
• A screen shot showing these menu options is given below
97
98
Implementation
• On the overhead following the next one, code for the new Visualization2 class is given
• In that code you will notice that the listeners for the menu options come from the VisMediator
• The code for VisMediator will be pursued after the code for Visualization2
99
• Depending on how closely you’re following the book’s materials, the following observations may also be important
• In addition to the printed code in the text, source code can be downloaded from the book’s Web site
• Unfortunately, the Visualization2 and VisMediator code on the Web site differ from that given in the book
• What is presented here is based on the printed version in the text
100
• package com.oozinoz.visualization;
• import java.awt.event.*;• import javax.swing.*;• import com.oozinoz.ui.*;
• public class Visualization2 extends Visualization • {• public Visualization2(UI ui) • {• super(ui);• }
101
• public JMenuBar menus() • {• JMenuBar menuBar = new JMenuBar();• JMenu menu = new JMenu("File");• menuBar.add(menu);
• JMenuItem menuItem = new JMenuItem("Save As...");• menuItem.addActionListener(mediator.saveAction());• menu.add(menuItem);
• menuItem = new JMenuItem("Restore From...");• menuItem.addActionListener(mediator.restoreAction());• menu.add(menuItem);
• return menuBar;• }
102
• public static void main(String[] args) • {• Visualization2 panel = new Visualization2(UI.NORMAL);• JFrame frame = SwingFacade.launch(panel, "Operational Model");• frame.setJMenuBar(panel.menus());• frame.setVisible(true);• }
103
• By this time it’s possible to have forgotten what, exactly, you’re saving
• What is being saved in serializable form are the lists of locations representing the current state of the visualization
• There is no stack present• It would be up to the user to name each separate
state as it’s saved, and restore it by name later, if desired
104
• On the overhead following the next one, the code for the saveAction() method of the VisMediator is given
• This is the method that is called in Visualization2 in order to add a listener to the menu item
• The book uses an anonymous listener (unfortunately)
105
• This anonymous listener relies on a save() method in VisMediator, which is also shown
• It is this method that finally does the actual work of saving using serializability
• You may want to take a look at the code as presented on the Web site
• It makes the relationship between the Visualization2 and VisMediator classes clearer
• The code from the text is given on the following overheads
106
• public ActionListener saveAction()• {• return new ActionListener()• {• public void actionPerformed()• {• try• {• VisMediator.this.save((Component)e.getSource());• }• catch(Exception ex)• {• System.out.println(“Failed save: “ + ex.getMessage());• }• }• };• }
107
• public void save(Component source) throws Exception • {• JFileChooser dialog = new JFileChooser();• dialog.showSaveDialog(source);• if (dialog.getSelectedFile() == null)• return;• • FileOutputStream out = null;• ObjectOutputStream s = null;• try • {• out = new FileOutputStream(dialog.getSelectedFile());• s = new ObjectOutputStream(out);• s.writeObject(factoryModel.getLocations());• } • finally • {• if (s != null)• s.close();• }• }
108
• Challenge 19.4• Write the code for the restoreAction() method
of the VisMediator class
109
• Solution 19.4• One solution is:• Comment mode on:• The challenge asks for the code for the
restoreAction() method• The solution shows the code for the restore()
method, without giving the restoreAction() method
110
• Comment mode continued:• The book notes that the restore() method (not
the restoreAction() method, since that’s not shown) is analogous to the save() method
• It would also be the case that the restoreAction() method would be analogous
• The code given by the book as a solution is shown on the next overhead
111
• public void restore(Component source) throws Exception • {• JFileChooser dialog = new JFileChooser();• dialog.showOpenDialog(source);• if (dialog.getSelectedFile() == null)• return;• • FileInputStream out = null;• ObjectInputStream s = null;• try • {• out = new FileInputStream(dialog.getSelectedFile());• s = new ObjectInputStream(out);• ArrayList list = (ArrayList) s.readObject();• factoryModel.setLocations(list);• } • finally • {• if (s != null)• s.close();• }• }
112
• Continuing with book’s material:• The book states that in the original book,
Design Patterns, the intent of the Memento design pattern is stated as follows:
• “Without violating encapsulation, capture and externalize an object’s internal state so that the object can be restored to this state later.”
113
• Challenge 19.5• In this case, we used Java serialization to write
to a file in binary format. • Suppose that we had written it to XML format
(textual) instead. • Write a short statement of whether, in your
opinion, saving a memento in textual form would violate encapsulation.
114
• Solution 19.5• To encapsulate is to limit access to an object’s
state and operations.• Saving an object, such as a collection of factory
[machine] location points, in textual form exposes the object’s data and allows anyone with a text editor to change the object’s state.
• Thus, saving an object in XML form violates encapsulation, at least to some degree.
115
• Violation of encapsulation through persistent storage may be a concern in practice, depending on your application.
• To address this threat, you might limit access to the data, as is common in a relational database.
• In other cases, you might encrypt the data, as is common when transmitting sensitive HTML text.
116
• The point here is not whether the words encapsulation and memento apply to a design but rather the real importance of ensuring data integrity while supporting the data’s storage and transmission.
117
• Comment mode on:• This is my take on what the authors have just
said:• The original question about storing in textual
form is a red herring• First of all, you might ask, why store object
state in textual form when Java serializability is available anyway?
118
• Secondly, their follow-up observation is essentially this:
• Even if you use serializability, somebody with a binary editor could still mess with the state of a stored object
• Ultimately, if you are going to save things in secondary storage, and security is a concern, you will have to limit access to the data
119
Another Example
• No other example will be given
120
Lasater’s UML
• Lasater’s UML diagram is included for the sake of completeness
• It doesn’t seem to add anything to understanding the pattern
121
122
Summary
• The Memento design pattern lets you capture an object’s state so that the object can be restored later
• The need to use the memento pattern is most frequently occasioned by the need to support an “undo” function in an application
• In this case, the state may be saved in an object which is part of the program and only persists as long as the program is running
123
• It may also be desirable to be able to save and restore the state of an object between program runs
• Potentially, such storage can persist for long periods of time
• Typically, the solution is to save a representation of the object to secondary storage
• Java serializability supports this capability
124
The End