Home > Articles > Software Development & Management

Design Patterns in Java: Memento

The Memento pattern lets you capture an object’s state so you can restore the object later. The authors of Design Patterns in Java explain how the Memento Pattern works.
This chapter is from the book

Sometimes, the object you want to create is one that existed previously. This occurs when you want to let a user undo operations, revert to an earlier version of work, or resume previously suspended work.

The intent of the Memento pattern is to provide storage and restoration of an object’s state.

A Classic Example: Using Memento for Undo

Chapter 17, Abstract Factory, introduced a visualization application that lets users perform operational modeling experiments with material flow through a factory. Suppose that the functionality for the Undo button has not yet been implemented. We can apply the Memento pattern to make the Undo button work.

A memento is an object that holds state information. In the visualization application, the state we need to preserve is that of the application. Whenever adding or moving a machine, a user should be able to undo that change by clicking Undo. To add the undo function to the visualization, we will need to decide how to capture the state of the application in a memento. We will also have to decide when to capture this state, and how to restore the application to a previously captured state. When the application starts up, it appears as in Figure 19.1.

Figure 19.1 When the visualization application starts up, the work panel is blank, and the Undo button is disabled.

The visualization starts up in an empty state, which is a state. Any time that the visualization is in this empty state, the Undo button should be disabled. After a few adds and drags, the visualization might appear as in Figure 19.2.

Figure 19.2 Users can add and rearrange machines in the visualization.

The state that we need to capture in a memento consists of a list of the locations of the machines that the user has placed. We can keep a stack of these mementos, popping one each time the user clicks Undo.

  • Each time the user adds or moves a machine in the visualization, your code will create a memento of the simulated factory and add it to a stack.
  • Each time the user clicks the Undo button, your code will pop the most recent memento and then restore the simulation to the state stored at the top of the stack.

When your visualization starts up, you will stack an initial, empty memento and never pop it, to ensure that the top of the stack is always a valid memento. Any time the stack contains only one memento, you disable the Undo button.

We might write the code for this program in a single class, but we expect to add features that support operational modeling and other features that your users may request. Eventually, the application may grow large, so it is wise to use an MVC design that you can build on. Figure 19.3 shows a design that moves the work of modeling the factory into a separate class.

Figure 19.3 This design divides the application’s work into separate classes for modeling the factory, providing GUI elements, and handling a user’s clicks.

This design lets you first focus on developing a FactoryModel class that has no GUI controls and no dependency on the GUI.

The FactoryModel class is at the center of our design. It is responsible for maintaining the current configuration of machines and for maintaining mementos of previous configurations.

Each time a client asks the factory to add or move a machine, the factory will create a copy—a memento—of its current locations and push this onto its stack of mementos. In this example, we do not need a special Memento class. Each memento is merely a list of points: the list of machine locations at a particular time.

The factory model must provide events that let clients register interest in changes to the factory state. This lets the visualization GUI inform the model of changes that the user makes. Suppose that you want the factory to let clients register for the events of adding a machine and dragging a machine. Figure 19.4 shows a design for a FactoryModel class.

Figure 19.4 The FactoryModel class maintains a stack of factory configurations and lets clients register for notification of changes in the factory.

The design in Figure 19.4 calls for the FactoryModel class to provide clients with the ability to register for notification of several events. For example, consider the event of adding a machine. Any registered ChangeListener object will be notified of this change:

package com.oozinoz.visualization;
// ...
public class FactoryModel {
    private Stack mementos;
    private ArrayList listeners = new ArrayList();

    public FactoryModel() {
        mementos = new Stack();
        mementos.push(new ArrayList()); 
    }
    //...
}

The constructor starts off the factory’s initial configuration as an empty list. The remaining methods in the class maintain the stack of machine configuration mementos and fire events that correspond to any changes. For example, to add a machine to the current configuration, a client can call the following method:

public void add(Point location) {
    List oldLocs = (List) mementos.peek();
    List newLocs = new ArrayList(oldLocs);
    newLocs.add(0, location);
    mementos.push(newLocs);
    notifyListeners();
}

This code creates a new list of machine locations and pushes the list on the stack of mementos that the factory model maintains. A subtlety here is that the code ensures that the new machine is first in this list. This is a clue to the visualization that a picture of this machine should appear in front of any other machines that the picture may overlap.

A client that registers for the change notification might update its view of the factory model by rebuilding itself entirely on receiving any event from the factory model. The factory model’s latest configuration is always available from getLocations(), whose code is as follows:

	public List getLocations() {
    return (List) mementos.peek();
}

The undo() method of the FactoryModel class lets a client change the model of machine locations to be a previous version. When this code executes, it also calls notifyListeners().


Challenge 19.1

Write the code for the FactoryModel class’s undo() method.

A solution appears on page 400.


An interested client can provide undo support by registering as a listener, supplying a method that rebuilds the client’s view of the factory. The Visualization class is one such client.

The MVC design in Figure 19.3 separates the tasks of translating user actions from the tasks of maintaining the GUI. The Visualization class creates its GUI controls but passes off the handling of GUI events to a mediator. The VisMediator class translates GUI events into appropriate changes in the factory model. When the model changes, the GUI may need to update. The Visualization class registers for the notification that the FactoryModel class provides. Note the division of responsibility.

  • The visualization changes factory events into GUI changes.
  • The mediator translates GUI events into factory changes.

Figure 19.5 shows the three collaborating classes in more detail.

Figure 19.5 The mediator translates GUI events into factory model changes, and the visualization reacts to factory events to update the GUI.

Suppose that while dragging a machine image, a user accidentally drops it in the wrong spot and clicks Undo. To be able to handle this click, the visualization registers the mediator for notification of button events. The code for the Undo button in the Visualization class is:

protected JButton undoButton() {
    if (undoButton == null) {
        undoButton = ui.createButtonCancel();
        undoButton.setText("Undo");
        undoButton.setEnabled(false);
        undoButton.addActionListener(mediator.undoAction());
    }
    return undoButton;
}

This code passes responsibility for handling a click to the mediator. The mediator informs the factory model of any requested changes. The mediator translates an undo request to a factory change with the following code:

	private void undo(ActionEvent e) {
        factoryModel.undo();
}

The factoryModel variable in this method is an instance of FactoryModel that the Visualization class creates and passes the mediator in the VisMediator class’s constructor. We have already examined the FactoryModel class’s pop() command. The flow of messages that occurs when the user clicks Undo appears in Figure 19.6.

Figure 19.6 This diagram shows the message flow that occurs when the user clicks Add.

When the FactoryModel class pops its current configuration, exposing the previous configuration it stored as a memento, the undo() method notifies any ChangeListeners. The Visualization class registers for this in its constructor:

	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);
}

For each machine location in the factory model, the visualization maintains a Component object that it creates with its createPictureBox() method. The stateChanged() method must clear all the picture box controls from the machine panel, and re-add new picture boxes from the current set of locations in the factory model. The stateChanged() method must also disable the Undo button if the factory has only a single memento left on its stack.


Challenge 19.2

Write the stateChanged() method for the Visualization class.

A solution appears on page 401.


The Memento pattern lets you save and restore an object’s state. A common use of Memento is related to providing undo support in applications. In some applications, as in the factory visualization example, the repository for the information you need to store can be another object. In other cases, you may need to store mementos in a more durable form.

InformIT Promotional Mailings & Special Offers

I would like to receive exclusive offers and hear about products from InformIT and its family of brands. I can unsubscribe at any time.

Overview


Pearson Education, Inc., 221 River Street, Hoboken, New Jersey 07030, (Pearson) presents this site to provide information about products and services that can be purchased through this site.

This privacy notice provides an overview of our commitment to privacy and describes how we collect, protect, use and share personal information collected through this site. Please note that other Pearson websites and online products and services have their own separate privacy policies.

Collection and Use of Information


To conduct business and deliver products and services, Pearson collects and uses personal information in several ways in connection with this site, including:

Questions and Inquiries

For inquiries and questions, we collect the inquiry or question, together with name, contact details (email address, phone number and mailing address) and any other additional information voluntarily submitted to us through a Contact Us form or an email. We use this information to address the inquiry and respond to the question.

Online Store

For orders and purchases placed through our online store on this site, we collect order details, name, institution name and address (if applicable), email address, phone number, shipping and billing addresses, credit/debit card information, shipping options and any instructions. We use this information to complete transactions, fulfill orders, communicate with individuals placing orders or visiting the online store, and for related purposes.

Surveys

Pearson may offer opportunities to provide feedback or participate in surveys, including surveys evaluating Pearson products, services or sites. Participation is voluntary. Pearson collects information requested in the survey questions and uses the information to evaluate, support, maintain and improve products, services or sites, develop new products and services, conduct educational research and for other purposes specified in the survey.

Contests and Drawings

Occasionally, we may sponsor a contest or drawing. Participation is optional. Pearson collects name, contact information and other information specified on the entry form for the contest or drawing to conduct the contest or drawing. Pearson may collect additional personal information from the winners of a contest or drawing in order to award the prize and for tax reporting purposes, as required by law.

Newsletters

If you have elected to receive email newsletters or promotional mailings and special offers but want to unsubscribe, simply email information@informit.com.

Service Announcements

On rare occasions it is necessary to send out a strictly service related announcement. For instance, if our service is temporarily suspended for maintenance we might send users an email. Generally, users may not opt-out of these communications, though they can deactivate their account information. However, these communications are not promotional in nature.

Customer Service

We communicate with users on a regular basis to provide requested services and in regard to issues relating to their account we reply via email or phone in accordance with the users' wishes when a user submits their information through our Contact Us form.

Other Collection and Use of Information


Application and System Logs

Pearson automatically collects log data to help ensure the delivery, availability and security of this site. Log data may include technical information about how a user or visitor connected to this site, such as browser type, type of computer/device, operating system, internet service provider and IP address. We use this information for support purposes and to monitor the health of the site, identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents and appropriately scale computing resources.

Web Analytics

Pearson may use third party web trend analytical services, including Google Analytics, to collect visitor information, such as IP addresses, browser types, referring pages, pages visited and time spent on a particular site. While these analytical services collect and report information on an anonymous basis, they may use cookies to gather web trend information. The information gathered may enable Pearson (but not the third party web trend services) to link information with application and system log data. Pearson uses this information for system administration and to identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents, appropriately scale computing resources and otherwise support and deliver this site and its services.

Cookies and Related Technologies

This site uses cookies and similar technologies to personalize content, measure traffic patterns, control security, track use and access of information on this site, and provide interest-based messages and advertising. Users can manage and block the use of cookies through their browser. Disabling or blocking certain cookies may limit the functionality of this site.

Do Not Track

This site currently does not respond to Do Not Track signals.

Security


Pearson uses appropriate physical, administrative and technical security measures to protect personal information from unauthorized access, use and disclosure.

Children


This site is not directed to children under the age of 13.

Marketing


Pearson may send or direct marketing communications to users, provided that

  • Pearson will not use personal information collected or processed as a K-12 school service provider for the purpose of directed or targeted advertising.
  • Such marketing is consistent with applicable law and Pearson's legal obligations.
  • Pearson will not knowingly direct or send marketing communications to an individual who has expressed a preference not to receive marketing.
  • Where required by applicable law, express or implied consent to marketing exists and has not been withdrawn.

Pearson may provide personal information to a third party service provider on a restricted basis to provide marketing solely on behalf of Pearson or an affiliate or customer for whom Pearson is a service provider. Marketing preferences may be changed at any time.

Correcting/Updating Personal Information


If a user's personally identifiable information changes (such as your postal address or email address), we provide a way to correct or update that user's personal data provided to us. This can be done on the Account page. If a user no longer desires our service and desires to delete his or her account, please contact us at customer-service@informit.com and we will process the deletion of a user's account.

Choice/Opt-out


Users can always make an informed choice as to whether they should proceed with certain services offered by InformIT. If you choose to remove yourself from our mailing list(s) simply visit the following page and uncheck any communication you no longer want to receive: www.informit.com/u.aspx.

Sale of Personal Information


Pearson does not rent or sell personal information in exchange for any payment of money.

While Pearson does not sell personal information, as defined in Nevada law, Nevada residents may email a request for no sale of their personal information to NevadaDesignatedRequest@pearson.com.

Supplemental Privacy Statement for California Residents


California residents should read our Supplemental privacy statement for California residents in conjunction with this Privacy Notice. The Supplemental privacy statement for California residents explains Pearson's commitment to comply with California law and applies to personal information of California residents collected in connection with this site and the Services.

Sharing and Disclosure


Pearson may disclose personal information, as follows:

  • As required by law.
  • With the consent of the individual (or their parent, if the individual is a minor)
  • In response to a subpoena, court order or legal process, to the extent permitted or required by law
  • To protect the security and safety of individuals, data, assets and systems, consistent with applicable law
  • In connection the sale, joint venture or other transfer of some or all of its company or assets, subject to the provisions of this Privacy Notice
  • To investigate or address actual or suspected fraud or other illegal activities
  • To exercise its legal rights, including enforcement of the Terms of Use for this site or another contract
  • To affiliated Pearson companies and other companies and organizations who perform work for Pearson and are obligated to protect the privacy of personal information consistent with this Privacy Notice
  • To a school, organization, company or government agency, where Pearson collects or processes the personal information in a school setting or on behalf of such organization, company or government agency.

Links


This web site contains links to other sites. Please be aware that we are not responsible for the privacy practices of such other sites. We encourage our users to be aware when they leave our site and to read the privacy statements of each and every web site that collects Personal Information. This privacy statement applies solely to information collected by this web site.

Requests and Contact


Please contact us about this Privacy Notice or if you have any requests or questions relating to the privacy of your personal information.

Changes to this Privacy Notice


We may revise this Privacy Notice through an updated posting. We will identify the effective date of the revision in the posting. Often, updates are made to provide greater clarity or to comply with changes in regulatory requirements. If the updates involve material changes to the collection, protection, use or disclosure of Personal Information, Pearson will provide notice of the change through a conspicuous notice on this site or other appropriate way. Continued use of the site after the effective date of a posted revision evidences acceptance. Please contact us if you have questions or concerns about the Privacy Notice or any objection to any revisions.

Last Update: November 17, 2020